2009-08-04 118 views
3

我需要使用單個linq查詢來篩選linq中實體的子元素。這可能嗎?如何篩選Linq中的子集合

假設我有兩個相關的表。詩歌和VerseTranslations。由LINQ to SQL創建的實體是這樣的,我有一個Verse對象,它包含一個VerseTranslation集合的子對象。

現在,如果我有後續的LINQ查詢

var res = from v in dc.Verses 
        where v.id = 1 
        select v; 

我得到詩篇的集合,其標識爲1,每節經文對象包含所有子從VerseTranslations對象。

我還想要做的是過濾Verse Translations的子列表。

到目前爲止,我已經能夠想出的唯一方法是通過使用新的類型匿名或其他。具體如下

var res= from v in dc.Verses 
        select new myType 
        { 
         VerseId = v.VerseId, 
         VText = v.Text, 
         VerseTranslations = (from trans in v.VerseTranslations 
               where languageId==trans.LanguageId 
               select trans 
        }; 

上面的代碼工作,但我不得不宣佈一個新的類吧。有沒有辦法做到這一點,這樣的子表上的過濾可以納入第一個linq查詢,以便不需要聲明新的類。

問候, MAC

回答

8

所以我終於得到它的工作得益於設拉子給出的指針。

 DataLoadOptions options = new DataLoadOptions(); 
     options.AssociateWith<Verse>(item => item.VerseTranslation.Where(t => languageId.Contains(t.LanguageId))); 

     dc.LoadOptions = options; 

     var res = from s in dc.Verse 
        select s; 

這不需要投影或使用新的擴展類。

感謝您輸入的所有人。

0

如果這是從一個數據庫來了,你可以運行你的第一個語句。

然後用Where子句進行加載或包含VerseTranslations。

http://msdn.microsoft.com/en-us/library/bb896249.aspx

你有在詩和VerseTranslations之間模型的關係。在這種情況下,這可能工作:

var res= from v in 
dc.Verses.Include("VerseTranslations").Where(o => languageId==o.LanguageId) 
select v; 
+0

從我的理解鏈接,其示例btw獲取單個對象,然後做一個負載。在我的情況下,我有一個收藏在水庫,我仍然無法適應我的情況。也許它缺乏睡眠:/ – MAC 2009-08-04 10:48:00

+0

好..啞巴問題......但我找不到包含方法! 是它來自數據庫和兩個表有關係。 所以dc是datacontext,Verses是表格。但我不能獲得包含功能。 – MAC 2009-08-04 11:11:46

+0

你是使用實體框架還是LINQ到SQL? – 2009-08-04 11:23:19

0

過濾的對象的封閉集合,

var res = dc.Verses 
      .Update(v => v.VerseTranslations 
         = v.VerseTranslations 
          .Where(n => n.LanguageId == languageId)); 

通過使用擴展方法 「更新」 從HookedOnLinq

public static class UpdateExtensions { 

    public delegate void Func<TArg0>(TArg0 element); 

    /// <summary> 
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence. 
    /// </summary> 
    /// <typeparam name="TSource">The source element type.</typeparam> 
    /// <param name="source">The source sequence.</param> 
    /// <param name="update">The update statement to execute for each element.</param> 
    /// <returns>The numer of records affected.</returns> 
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update) { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (update == null) throw new ArgumentNullException("update"); 
     if (typeof(TSource).IsValueType) 
      throw new NotSupportedException("value type elements are not supported by update."); 

     int count = 0; 
     foreach(TSource element in source) { 
      update(element); 
      count++; 
     } 
     return count; 
    } 
} 
0

有沒辦法以這樣的方式做到這一點,例如 子表上的過濾可以合併到 第一個linq查詢中,以便不需要聲明新的 類?

從技術上講,答案是否定的。如果你想返回比單個實體對象(Verse,VerseTranslation)可以容納的數據更多的數據,你需要某種對象來「投影」。然而,讓您可以通過使用匿名類型明確聲明myType

var res = from v in dc.Verses 
      select new 
      { 
       Verse = v, 
       Translations = (from trans in v.VerseTranslations 
           where languageId==trans.LanguageId 
           select trans).ToList() 
      }; 

var first = res.First(); 
Console.WriteLine("Verse {0} has {1} translation(s) in language {2}.", 
    first.Verse.VerseId, first.Translations.Count, languageId); 

編譯器將生成相應類型的詩歌和翻譯屬性你的類。只要您不需要按名稱引用類型(例如,從命名方法返回),就可以將這些對象用於任何事物。因此,雖然技術上並非「聲明」某種類型,但您仍然在使用將根據您的規範生成的新類型。

就使用單個LINQ查詢而言,這一切都取決於您希望數據結構化的方式。對我來說,您的原始查詢似乎最有意義:將每個Verse與已過濾的翻譯列表配對。如果只希望每個語種一個翻譯,你可以使用SingleOrDefault(或FirstOrDefault)扁平化你的子查詢,或只使用一個SelectMany這樣的:

var res= from v in dc.Verses 
     from t in v.VerseTranslations.DefaultIfEmpty() 
     where t == null || languageId == t.LanguageId 
     select new { Verse = v, Translation = t }; 

如果詩有多種翻譯,這將返回「行「爲每個詩/翻譯對。我使用DefaultIfEmpty()作爲左連接,以確保我們將獲得所有Verses,即使他們錯過了翻譯。