3

単一の linq クエリを使用して、linq でエンティティの子要素をフィルター処理する必要があります。これは可能ですか?

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

次のlinqクエリがある場合

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

ID が 1 の Verses のコレクションを取得し、各 verse オブジェクトには VerseTranslations のすべての子オブジェクトが含まれています。

私がやりたいことは、Verse Translations の子リストをフィルタリングすることです。

これまでのところ、私が思いついた唯一の方法は、新しい Type Anonymous を使用するか、それ以外の方法です。次のように

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クエリに組み込むことができるような方法でそれを行う方法はありませんか?

よろしく、マック

4

4 に答える 4

9

それで、シラーズから与えられた指針のおかげで、ようやく機能するようになりました。

        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;

これには、プロジェクションや新しい拡張クラスの使用は必要ありません。

すべての入力者に感謝します。

于 2009-08-04T22:55:43.940 に答える
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);

コンパイラは、適切に入力されたVerseプロパティとTranslationsプロパティを使用してクラスを生成します。これらのオブジェクトは、名前で型を参照する必要がない限り(たとえば、名前付きメソッドから戻るため)、ほぼすべての目的で使用できます。したがって、技術的に型を「宣言」していなくても、仕様に従って生成される新しい型を使用していることになります。

単一のLINQクエリを使用する限り、データをどのように構造化するかによって異なります。私には、元のクエリが最も理にかなっているように思えます。それぞれVerseを、フィルタリングされた翻訳のリストと組み合わせてください。言語ごとに1つの翻訳しか期待できない場合は、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()は左の結合として使用して、翻訳が欠落している場合でもすべての詩を確実に取得できるようにします。

于 2009-08-04T12:53:46.463 に答える
0

これがデータベースからのものである場合は、最初のステートメントを実行できます。

次に、Where句を使用してVerseTranslationsのロードまたはインクルードを実行します。

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

モデル内でVerseとVerseTranslationsの間に関係がありますか。その場合、これは機能する可能性があります。

var res= from v in 
dc.Verses.Include("VerseTranslations").Where(o => languageId==o.LanguageId)
select v;
于 2009-08-04T10:17:43.477 に答える
0

オブジェクトの同封されたコレクションをフィルタリングし、

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

HookedOnLinqの拡張メソッド「Update」を使用する

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;
    }
}
于 2009-08-04T10:29:35.090 に答える