LINQ-to-SQLコンパイル済みクエリの問題(コンパイルされていないクエリとして機能)
-
07-07-2019 - |
質問
IQueryable
にC#拡張メソッドがあります。 FindNewCustomers()
および FindCustomersRegisteredAfter(int year)
など、「チェーン」に使用します。 LINQ to 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)であり、文字列に基づいて動的な式を作成します(例:&quot; FirstName ASC&quot;は、フィールドFirstNameを昇順でソートします)。 Skip
および Take
は組み込みメソッドです。
上記(コンパイル済みクエリではなく、通常のクエリとして)は perfect で機能します。コンパイル済みのクエリに入れると、次のエラーが発生しました。
メソッド 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [Customer](System.Linq.IQueryable`1 [Domain.Customer]、Int32)'にはSQLへの変換がサポートされていません。
もう一度、これはクエリがコンパイルされていない場合でも完全に機能します、通常のLINQクエリです。エラーは、CompiledQuery.Compile()内にある場合にのみ表示されます。
ヘルプ??!
編集:CompiledQuery.Compileの内部と同じ方法でvar query =(...)を使用してクエリを作成すると、これは生成されたSQLです。
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]
つまり、SQLはすべて完全に翻訳可能であることがわかります。そのため、@ p0、@ p1、および@ p2を入力するだけで、これが繰り返し機能します。 CompiledQuery.Compileの何が問題になっていますか?!?
更新:OrderByが機能しないことを理解しています(@pパラメーターではないため)。 CompiledQuery.Compileが私の拡張メソッドで動作しない理由をまだ解明しようとしています。このトピックに関するインターネット上の情報は事実上存在しません。
解決
コンパイルされたクエリはSQLに翻訳可能である必要がありますが、これは拡張メソッドでは不可能です。 「通常」で作成されたSQLのプロファイルを作成する場合クエリは、テーブル全体を選択しているため、すべての行を拡張メソッドにフィードできます。
クエリに(式ツリーの一部として)フィルタリングロジックを配置して、SQLに変換してサーバー側で実行できるようにする方がよいでしょう。
OrderByもSkipのため問題です。これをSQLに変換可能にする必要があります。そうしないと、LINQは、クライアント側でフィルターをかけるためにすべての行を返す必要があります。
これらをLINQ式として表現できない場合は、サーバーでSQL関数を作成し、DataContextにマッピングすることを検討してください。 LINQはこれらをT-SQL関数呼び出しに変換できます。
編集:
私はあなたの拡張メソッドが式ツリーを構築していないと仮定していたと思います。ごめんなさい。
これを検討リンク問題に似ているようです。さらに詳しく説明する別のリンクを参照します。
MethodCallExpressionが問題のようです。
コードは投稿するのに少し長い ここでは、tomasp.netの エキスパンダー、私はすべての表現を訪問します 式ツリーとノードが を呼び出すMethodCallExpression 式を返すメソッド ツリー、私はそれを置き換えます 式によるMethodCallExpression メソッドの呼び出しによって返されるツリー。
したがって、クエリのコンパイル時にメソッドが実行されないため、SQLに変換する式ツリーがないという問題があるようです。