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

8

我在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),它基于字符串创建动态表达式(例如,“FirstName ASC”将按照FirstName字段升序排序)。而SkipTake则是内置的方法。

以上代码(不是编译查询,而是普通查询)工作得很好。但一旦放入编译查询中,就会遇到以下错误:

“System.Linq.IQueryable`1[Domain.Customer] FindCustomersRegisteredAfter[Customer](System.Linq.IQueryable`1[Domain.Customer], Int32)”方法没有被支持转换为SQL。

再次强调,如果查询是非编译的,它可以正常工作。这个错误只会在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一起使用。对于这个主题,网上的信息几乎不存在。


5
我不明白为什么这是社区 Wiki。 - JustLoren
1个回答

3
我认为编译查询必须能够转换为 SQL,而您的扩展方法不能。如果您对“常规”查询创建的 SQL 进行分析,您可能会发现它在选择整个表,以便将所有行馈送到您的扩展方法中。
您最好将筛选逻辑放在查询中(作为表达式树的一部分),这样可以将其转换为 SQL 并在服务器端运行。
由于 Skip 的存在,OrderBy 也是一个问题。您需要使它能够转换为 SQL,否则 LINQ 将不得不返回所有行以便在客户端进行过滤。
如果无法将它们表示为 LINQ 表达式,请考虑在服务器上创建 SQL 函数并将其映射到您的 DataContext。LINQ 将能够将它们转换为 T-SQL 函数调用。
编辑:
我猜您的扩展方法没有构建表达式树。抱歉。
考虑这个链接,它似乎与你的问题相似。它引用了另一个链接,详细介绍了更多内容。 看起来MethodCallExpression是问题所在。 引用如下: 代码有点长,无法在此处贴出,但类似于tomasp.net的扩展程序,我访问表达式树中的每个表达式,并且如果节点是调用返回表达式树的方法的MethodCallExpression,则将该MethodCallExpression替换为通过调用方法返回的表达式树。 因此,问题似乎在于编译查询时未执行该方法,因此没有表达式树可翻译为SQL。

当我创建一个断点时,我可以看到SQL可以很容易地完整生成(包括跳过、取出、排序等),只要它不是一个编译查询。 - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接