3

さまざまな数のテーブルに対して非常に動的なLinqクエリを作成する必要があります。たとえば、関連するテーブルがあります:
Table_A
--ID
--Name
--Desc

Table_B
-ID
-Table_A_ID-
名前
-説明

Table_C
-ID
-Table_B_ID-
名前
-説明

テーブルの依存関係に関する情報を含むディクショナリがあります:
  tableName、parentTableName、foreignKey、parentPK
例:
  "Table_B"、 "Table_A"、 "Table_A_ID"、 "ID"
  "Table_C"、 "Table_B"、 "Table_B_ID"、 "ID 「」

->tableInfo["Table_B"]。ForeignKeyは"Table_A_ID"などを返します。

これで、ユーザーは表示する列を選択できます。
例:

  • Table_B.Name、Table_C.Desc、
     または
  • Table_A.Name、Table_B.Name、
     または
  • Table_A.Name、Table_B.Name、Table_B.Desc、Table_C.Name

    この選択は別のリストで利用できます。
    たとえば、選択3の場合:
    viewInfo["Table_A"]には"Name"が含まれます
    viewInfo["Table_B"]には"Name"が含まれ、 "Desc"
    viewInfo["Table_C"]には"Name"が含まれます

    必要なテーブルとフィールドを使用してクエリを動的に作成し、目的の結果を得るにはどうすればよいですか?

  • 4

    3 に答える 3

    4

    私が取り組んでいるプロジェクトでも同じことを行いました。ユーザーがUIで行った選択に基づいて、実行時にクエリが完全に作成されます。

    System.Linq.Expressions名前空間のクラスを使用して、式ツリーを使用してLINQクエリを作成します。非常に強力ですが、学習曲線が急です。

    LINQPadを使用してクエリを記述し、式をダンプして、下にあるツリーがどのように見えるかを確認して、クエリを自分で作成する方法を知ることができます。

    たとえば、LINQPadで次のコードを実行すると、式ツリーのダンプが生成されます。

    var query = from p in Puzzles
    select p;
    
    query.Expression.Dump(20);
    

    LINQPadスクリーンショット

    では、単純なLINQクエリを動的に作成するコードを実際にどのように作成するのでしょうか。

    最も単純なクエリである次の例を考えてみましょう。

    var query = from person in data
       select person;
    

    次のコードは、同等のクエリをその場で生成します。

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    
    namespace TestLinqGenerator
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Set up dummy data
                var data = new[]
                               {
                                   new {Name = "Fred"},
                                   new {Name = "Simon"}
                               }.AsQueryable();
                var dataType = data.ElementType;
    
                // IQueryable: data
                var source = Expression.Constant(data);
    
                // Parameter: person
                var parameter = Expression.Parameter(dataType, "person");
    
                // person => person
                var lambda = Expression.Lambda(parameter, parameter);
    
                // Expression: data.Select(person => person)
                var callSelect = Expression.Call(GetSelect().MakeGenericMethod(dataType, dataType), source, Expression.Quote(lambda));
    
                // IQueryable: data.Select(person => person)
                var query = data.Provider.CreateQuery(callSelect);
    
                // Execute query
                var results = query.Cast<object>().ToList();
    
            }
    
            private static MethodInfo GetSelect()
            {
                // Get MethodInfo of the following method from System.Linq.Queryable:
                // public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
                return typeof(System.Linq.Queryable).GetMethods().Where(
                    method => method.Name == "Select" && method.GetParameters().Length == 2 &&
                              method.GetParameters()[1].ParameterType.GetGenericArguments()[0].Name == typeof(Func<,>).Name).Single();
            }
    
        }
    }
    

    このコードをコンソールアプリケーションに貼り付けることで、このコードを実行できるはずです。デバッガーをステップスルーして、各ステップの機能を理解します。

    追加情報

    Reflectorの使用の実装をQueryable.Select確認すると、クエリを動的に作成するときに何が必要かを理解するのに役立ちます。以下にコピーしました:

    public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, int, TResult>> selector)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (selector == null)
        {
            throw Error.ArgumentNull("selector");
        }
        return source.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TResult) }), new Expression[] { source.Expression, Expression.Quote(selector) }));
    }
    

    興味深いことに、の実装は、Queryable.Selectそれ自体を呼び出すLINQ式表現を作成するだけです。LINQプロバイダーは、実際にその式を別のTSQLに変換します。メソッド自体はSelect実際には選択を実行しません。

    コードも同じことを行う必要があります-LINQ式を作成します。

    単純な選択を行う方法に慣れたらQueryable.Where、LINQクエリのミックスやその他の機能に追加する方法を検討できます。投影(select new {x, y, z}など)は非常に難しいので、長持ちさせることをお勧めします。コンパイラが匿名型を生成するのとほぼ同じ方法で、実行時に型を生成する必要があります。System.Reflection.Emit仕事のためのあなたのツールです。

    このアプローチの優れた点の1つは、LINQ to Entities、LINQ to SQL、Mindscape Lightspeed、によって提供されるメモリ内LINQプロバイダー実装などの任意のLINQプロバイダーで使用できることですAsQueryable

    LINQ式を生成する私のコードはIQueryableを受け入れ、実行時にこれは現在Mindscape Lightspeed IQueryablesで提供されていますが、他の1つである可能性もあります。次に、単体テストで、オブジェクトの配列を使用してテストデータを作成し、それをLINQ式ジェネレーターに渡されるIQueryableusingに変換します。AsQueryable私の単体テストでは、あらゆる範囲の複雑なクエリを生成できますが、データベースを必要とせずに簡単にテストできます。上記のサンプルは、これを行う方法を示しています。

    于 2010-08-17T14:11:55.893 に答える
    3

    クエリを動的に構築するのに役立つDynamicLINQというプロジェクトがあります。このプロジェクトを見てみるべきだと思います。

    それ以外に、LINQクエリをクエリすることで部分的にクエリを作成することもできます。コードに条件ステートメントを含めることができます。分岐が続く場合は、既存のクエリから再度クエリを実行して新しいクエリを作成できます。結果をリクエストするまでクエリは実行されないため、クエリを小さな部分にまとめたり、最初から1つの大きなクエリを作成したりしても、パフォーマンスの面ではそれほど重要ではありません。この手法を使用すると、(入力の値に基づいて)静的型付けとインテリセンスの利点を活用しながら、いくつかの共通部分を共有する構造的に異なるクエリを構築できます。

    于 2010-08-17T13:59:13.707 に答える
    1

    Codeplexにある非常に興味深いフレームワークNLinqを使用して問題を解決しました。「通常の」Linqクエリを含む文字列を作成する必要があります。

    プロジェクトの説明からの引用:

    NLinqは、Linq文法パーサーと「LinqTo Objects」実行環境を提供することにより、Visual Studio.Net2003およびVisualStudio2005(C#およびVB .Net)でLinq機能を再実装することに焦点を当てたフレームワークです。NLinqを使用すると、C#3.0の主要な機能を必要とせずに今すぐ利用できます。

    例:

    Data sources used for the samples
            Person[] people = new Person[] { 
                new Person("Bill", 31), 
                new Person("John", 30), 
                new Person("Cindy", 25), 
                new Person("Sue", 29) 
            };
    
            // For testing physical links
            people[0].Friends.Add(people[0]);
            people[0].Friends.Add(people[1]);
            people[1].Friends.Add(people[2]);
            people[2].Friends.Add(people[3]);
            people[3].Friends.Add(people[0]);
    
            // For testing logical links
            Address[] addresses = new Address[] {
                new Address("Bill", "Redmon"),
                new Address("Bill", "Boston"),
                new Address("Cindy", "New York")
            };
    
    Projections query = new NLinqQuery(
                    @"  from c in people 
                        from d in people
                        where c.Age > d.Age
                        select new NLinq.Play.Person ( c.Firstname, d.Age )");
    
            linq = new LinqToMemory(query);
            linq.AddSource("people", people);
    
    
    Result:
    Sue (25)
    John (25)
    John (29)
    Bill (30)
    Bill (25)
    Bill (29)
    
    于 2010-08-23T13:51:45.467 に答える