2009-08-04 15 views
3

linqのエンティティの子要素を1つのlinqクエリでフィルタリングする必要があります。これは可能ですか?Linqの子コレクションをフィルタリングする方法

2つの関連テーブルがあるとします。詩と詩の翻訳。 LINQ to SQLによって作成されたエンティティは、VerseTranslationのコレクションである子オブジェクトを含むVerseオブジェクトを持っています。私はフォローのLINQクエリ

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

を持っている場合

は今、私は、そのIDが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は単一のオブジェクトを取得してから、Loadを実行します。私の場合、私はresのコレクションを持っていますが、私はまだ私のシナリオにそれを適応させることができませんでした。たぶんその睡眠の欠如:/ – MAC

+0

ok..dumb質問...私はインクルードメソッドを見つけることができません! はいこれはデータベースからのもので、2つのテーブルには関係があります。 したがって、dcはdatacontextであり、Versesはテーブルです。私はインクルード関数を取得することはできません。 – MAC

+0

エンティティフレームワークまたはlinqをSQLに使用していますか? –

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

これには実際に組み込まれているメカニズムはありませんか? – MAC

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); 

コンパイラを使用するための適切な型指定された詩と翻訳プロパティを持つクラスを生成します。名前でタイプを参照する必要がないかぎり(名前付きメソッドから戻るなど)、これらのオブジェクトは何でも使用できます。したがって、あなたは技術的に型を宣言していませんが、あなたは仕様に従って生成される新しい型を使用しています。

1つのLINQクエリを使用する限り、すべてのデータがどのように構造化されるかによって異なります。私にとっては、元のクエリが最も理にかなっているようです:Verseには、フィルタリングされた翻訳リストを組み合わせてください。あなたは、言語ごとに1つだけの翻訳を期待していた場合、あなたはあなたのサブクエリをフラット化、またはちょうどこのようSelectManyを使用するSingleOrDefault(またはFirstOrDefault)を使用することができます

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()を左の結合として使用して、翻訳が欠けていてもすべての節を得ることを確認します。

+1

答えに危険な「=」があります。「==」にする必要があります。 – Timwi

+0

良いキャッチ固定 – dahlbyk

関連する問題