8

IQueryableたとえばFindNewCustomers()FindCustomersRegisteredAfter(int year)LINQ to SQL のクエリを「チェーン」するために使用するC# 拡張メソッドなどがあります。

今私の問題に:私はコンパイルされたクエリを作成したい、例えば:

 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は組み込みメソッドです。

上記(コンパイルされたクエリではなく、通常のクエリ)は完璧に機能します。コンパイルされたクエリに入れると、次のエラーが発生します。

メソッド 'System.Linq.IQueryable`1[Domain.Customer] FindCustomersRegisteredAfter[Customer](System.Linq.IQueryable`1[Domain.Customer], Int32)' には、SQL への変換がサポートされていません。

繰り返しになりますが、クエリがコンパイルされていない場合、これは完全に機能し、通常の LINQ クエリだけです。エラーは、CompiledQuery.Compile() 内にある場合にのみ表示されます。

ヘルプ??!

編集: var query = (...) CompiledQuery.Compile の内部と同じ方法でクエリを作成すると、次の 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 が拡張メソッドで機能しない理由をまだ理解しようとしています。このトピックに関するインターネット上の情報は事実上存在しません。

4

1 に答える 1

3

コンパイルされたクエリはSQLに翻訳可能でなければならないと思いますが、拡張メソッドでは翻訳できません。「通常の」クエリによって作成されたSQLのプロファイルを作成すると、テーブル全体が選択されているため、すべての行を拡張メソッドにフィードできる場合があります。

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

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

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

編集:

私はあなたの拡張メソッドが式ツリーを構築していないと思っていたと思います。ごめん。

あなたの問題に似ているように見えるこのリンクを考えてみてください。これは、より詳細に説明されている別のリンクを参照しています。

MethodCallExpressionが問題のようです。

コードはここに投稿するには少し長いですが、tomasp.netのエクスパンダーと同様に、式ツリー内のすべての式にアクセスし、ノードが式ツリーを返すメソッドを呼び出すMethodCallExpressionである場合は、そのMethodCallExpressionをメソッドを呼び出すことによって返される式ツリー。

したがって、問題は、クエリをコンパイルするときにメソッドが実行されないため、SQLに変換する式ツリーがないことです。

于 2009-09-14T17:50:07.630 に答える