2009-09-14 2 views
8

IQueryableにはC#拡張メソッドがあります。 FindNewCustomers()FindCustomersRegisteredAfter(int year)など、LINQ to SQLのクエリをまとめてチェーン化するために使用します。私の問題への今LINQからSQLへのコンパイルされたクエリの問題(コンパイルされていないクエリとして機能します)

:私は例えば、コンパイルされたクエリを作成したい。:

private static Func<MyDataContext, SearchInfo, IQueryable<Customer>> 
     CQFindAll = 
      CompiledQuery.Compile((MyDataContext dc, SearchInfo info) => 
       dc.Contacts.Select(c => c).FindCustomersRegisteredAfter(info.RegYear) 
          .OrderBy(info.OrderInfo) 
          .Skip(info.SkipCount) 
          .Take(info.PageSize)); 

FindCustomersRegisteredAfter(int year)方法は、拡張メソッドIQueryableを取り、同じを返すです。 OrderByメソッドは、文字列に基づいて動的式を作成する拡張メソッド(System.Linq.Dynamic)でもあります(たとえば、「FirstName ASC」はFirstNameを昇順にソートします)。 SkipおよびTakeは組み込みのメソッドです。

上記は(コンパイルされたクエリではありませんが、通常のクエリでは)が完璧ですです。私がコンパイルされたクエリに入れたら、私は次のエラーを打つ:クエリは普通のLINQクエリ、非コンパイルされている場合

もう一度
Method 'System.Linq.IQueryable`1[Domain.Customer] FindCustomersRegisteredAfter[Customer](System.Linq.IQueryable`1[Domain.Customer], Int32)' has no supported translation to SQL.

これは完璧に動作します。このエラーは、CompiledQuery.Compile()の内部にあると一度だけ表示されます。

ヘルプ??

編集:

SELECT [t1].[Id], [t1].[FirstName], [t1].[LastName], 
     [t1].[RegYear], [t1].[DeletedOn] 
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[LastName]) AS [ROW_NUMBER], 
     [t0].[Id], [t0].[FirstName], [t0].[LastName], [t0].[RegYear], 
     [t0].[DeletedOn] 
FROM [dbo].[Contacts] AS [t0] 
WHERE ([t0].[RegYear] > @p0) AND ([t0].[DeletedOn] IS NULL) 
    ) AS [t1] 
WHERE [t1].[ROW_NUMBER] BETWEEN @p1 + 1 AND @p1 + @p2 
ORDER BY [t1].[ROW_NUMBER] 

だから、あなたがいることを参照してください。私はVARクエリ=(...)を経由してCompiledQuery.Compileの内側と同じように、クエリを作成する場合、これはSQLが生成されSQLは完全に翻訳可能なので、@ p0、@ p1、および@ p2は繰り返し入力するだけです。 CompiledQuery.Compileの何が問題なのですか?

更新:OrderByは(@pパラメータではないので)動作しないことを理解しています。私はまだCompiledQuery.Compileが私の拡張メソッドで動作しない理由を理解しようとしています。このトピックへのインターネットに関する情報は事実上存在しません。

+5

なぜこれがコミュニティのwikiであるのか分かりません。 – JustLoren

答えて

3

私は、コンパイルされたクエリはSQLに変換可能でなければならないと考えています。これはあなたの拡張メソッドではできません。 "通常の"クエリで作成されたSQLをプロファイルすると、すべての行を拡張メソッドに供給できるようにテーブル全体が選択されていることがわかります。

フィルタリングロジックを(式ツリーの一部として)クエリに追加すると、SQLに変換してサーバー側を実行できるようになります。

SkipのためにOrderByも問題です。これをSQLに変換可能にする必要があります。そうしないと、LINQはクライアント側でフィルタリングするためにすべての行を返す必要があります。

これらをLINQ式として表すことができない場合は、サーバーでSQL関数を作成し、DataContextにマッピングすることを検討してください。 LINQはそれらをT-SQL関数呼び出しに変換できます。

編集:私は私はあなたの拡張メソッドは、式ツリーを構築していなかったと仮定した

思います。ごめんなさい。

これはあなたの問題に似ていると思われるlinkを考えてください。これは、もう1つのlinkを参照しています。

MethodCallExpressionが問題であるようです。

The code is a bit long to be posted here, but similar to tomasp.net's expander, I visit every expression in the expression tree and if the node is a MethodCallExpression which calls a method that returns an expression tree, I replace that MethodCallExpression by the expression tree returned by invoking the method.

だから、問題は、クエリのコンパイル時にSQLに変換するいかなる式ツリーが存在しないので、メソッドが実行されていないということです表示されます。

+0

ブレークポイントを作成すると、コンパイルされたクエリでない限り、SQLを完全に(スキップ、テイク、オーダーバイなどを含む)生成することが容易にできることがわかります。 – Alex

+0

投稿を編集して、生成されたSQLを表示しました。 – Alex

関連する問題