2016-11-28 19 views
3

私は最初のEntity Frameworkのコードでは、次のことを行うしたいと思います:インナー方法

Data.GroupBy(x => x.Person.LastName) 
.Select(x => x.OtherGrouping(...)).Where(...).GroupBy(...)...etc 



public static class Extensions 
{ 

public static IQueryable<IGrouping<T, T>> OtherGrouping<T>(this IQueryable<T> collection /**** Here I want to pass consts and lambdas\expressions*****/) 
    { 
     // Grouping, filtration and other stuff here 
     return collection.GroupBy(x => x); 
    } 
} 

問題は、.NETはOtherGrouping方法は、それが表現のように表していないコンパイルされ、 SQLに変換できませんでした。

私はLinqKitというような場合に役立つライブラリを見つけましたが、私は特定のケースでどのようにそれを適用するかを理解できませんでした。私はちょうどx => x+2のような式を持っている単純なケースでうまく動作しますが、IQueryableの戻り値に悩まされます。おそらく、手で完全に表現木を書くことは可能ですが、私は深く行きたくありません。

どうすればいいのか、どこで読むことができるのでしょうか?ここで


は、私はロブのコメント

void Main() 
{ 
    AddressBases.GroupBy(x => x.ParentId).Select(x => new {x.Key, Items = x.AsQueryable().OtherGrouping(a => a.Country) }).Dump(); 

} 

public static class Extensions 
{ 
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) 
    { 
     return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
      source.GroupBy(keySelector).Expression); 
    } 
} 

に基づいて実行しようとしましたそして、私は例外を得たものである:

NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.Linq.IGrouping`2[System.String,InsuranceData.EF.AddressBase]] OtherGrouping[AddressBase,String](System.Linq.IQueryable`1[InsuranceData.EF.AddressBase], System.Linq.Expressions.Expression`1[System.Func`2[InsuranceData.EF.AddressBase,System.String]])' method, and this method cannot be translated into a store expression. 

私はOtherGroupingメソッドを持っている理由はまだ把握できませんでした表現木? OtherGroupingメソッドは、式に別のグループ化を追加し、それをプロバイダに渡しますが、ツリー自体には入れません。

+1

と交換する式の訪問者を記述する必要があります。例をご覧ください(https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/IQueryable.cs#L90)。 – Rob

+0

ありがとう@ロブ、LINQのソースコードを見てみることをお勧めします。私の愚かさの原因私はEFのソースと障害でそれを見つけようとしました。私はすぐにそれを試してみましょう、あなたはコメントの代わりに回答を投稿することができますので、私はそれを受け入れることができ、それは他の人に見えるようになります – Neir0

答えて

1

編集後の現在のコードが正しい - 間違っているところは、C#によって与えられた構文的な砂糖に関するよくある問題です。

次の記述した場合:あなたは私たちは、出力を得ることがわかります

void Main() 
{ 
    var res = Containers.OtherGrouping(c => c.ContainerID); 
    res.Expression.Dump(); 
    res.Dump(); 
} 

public static class Extensions 
{ 
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) 
    { 
     return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
      source.GroupBy(keySelector).Expression 
     ); 
    } 
} 

Table(Container).GroupBy(c => c.ContainerID) 

をし、その結果を適切に私たちの述語によってグループ化、問題に実行されます。しかし、Selectに渡すときにの中にOtherGroupingを入れて、の式を呼び出しています。さんはOtherGroupingない単純なケースを試してみましょう:

var res = Containers.OtherGrouping(c => c.ContainerID) 
    .Select(x => new 
    { 
     x.Key, 
     Items = x.GroupBy(c => c.ContainerID), 
    }); 
res.Expression.Dump(); 

何がSelectに提供しているのは、式ツリーです。つまり、内部GroupByのメソッドは実際には呼び出されません。クエリをx.AsQueryable().OtherGroupingに変更し、OtherGroupingにブレークポイントを設定すると、初めてヒットするだけです。

さらに、xはIEnumerable<T>であり、IQueryable<T>ではありません。 AsQueryable()を呼び出すとIQueryableが得られますが、新しいクエリプロバイダも提供されます。私はエンティティフレームワークの内部動作について100%確信していませんが、これは無効であると言いたいと思います。データベースがプロバイダのターゲットとして存在しないためです。実際、linq2sql(LINQPadを使用)では、.AsQueryable()がサポートされていないというエラーが表示されます。

あなたは何をすればよいですか?残念ながら、これを行うためのきれいな方法はありません。 OtherGroupingの前に表現木がで既に構築されているので、OtherGroupingの本体は何であるかは関係ありません。表現ツリーは構築後、実行前に変更する必要があります。そのために

、あなたはあなたがコレクションのクエリプロバイダのコールを呼び出すために必要があると思い.OtherGrouping表現の呼び出しを探し、そしてQueryable.GroupBy

+0

おそらく私たちは以下を行うことができます:OtherGroupingは式を返し、 for Invokeを使用してOtherGroupingメソッドを呼び出すと選択します。それから、LinqKitを選択して使用するだけでそれを渡します。魔法の変更を展開します。実際の式への呼び出しを呼び出します。 – Neir0