LINQ-to-SQL编译的查询问题(作为未编译的查询)

Ale*_*lex 8 c# linq expression-trees linq-to-sql compiled-query

我有C#扩展方法IQueryable,例如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));
Run Code Online (Sandbox Code Playgroud)

FindCustomersRegisteredAfter(int year)方法是一种采用IQueryable并返回相同的扩展方法.该OrderBy方法也是一种扩展方法(System.Linq.Dynamic),它根据字符串创建动态表达式(例如"FirstName ASC"将对字段FirstName进行排序).Skip并且Take是内置方法.

上面(不是编译查询,但常规查询)工作完美.一旦我把它放在编译的查询中,我就会遇到以下错误:

方法 'System.Linq.IQueryable`1 [Domain.Customer] FindCustomersRegisteredAfter [客户](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]
Run Code Online (Sandbox Code Playgroud)

所以你看到SQL完全可以翻译,所以我只需要填写@ p0,@ p1和@ p2就可以重复工作!CompiledQuery.Compile有什么问题?!?

更新:我理解OrderBy无法工作(因为它不是@p参数).我仍在试图弄清楚为什么CompiledQuery.Compile不适用于我的扩展方法.互联网上关于该主题的信息几乎不存在.

Don*_*yrd 3

我相信编译的查询必须可转换为 SQL,而您的扩展方法则不能。如果您分析由“常规”查询创建的 SQL,您可能会发现它正在选择整个表,以便它可以将所有行输入到您的扩展方法中。

您最好将过滤逻辑放入查询中(作为表达式树的一部分),以便将其转换为 SQL 并在服务器端运行。

由于 Skip,OrderBy 也是一个问题。您需要将其转换为 SQL 或 LINQ 将必须返回所有行,以便在客户端过滤它们。

如果您无法将这些表达为 LINQ 表达式,请考虑在服务器上创建 SQL 函数并将它们映射到 DataContext。LINQ 将能够将这些转换为 T-SQL 函数调用。

编辑:

我想我假设你的扩展方法没有构建表达式树。对不起。

考虑这个与您的问题类似的链接。它引用了另一个更详细的链接。

看来 MethodCallExpression 是问题所在。

在这里发布的代码有点长,但与 tomasp.net 的扩展器类似,我访问表达式树中的每个表达式,如果节点是调用返回表达式树的方法的 MethodCallExpression,我会将该 MethodCallExpression 替换为通过调用该方法返回的表达式树。

因此,问题似乎在于编译查询时,该方法未执行,因此没有表达式树可转换为 SQL。