【问题标题】:LINQ-to-SQL Compiled Query Problem (works as uncompiled query)LINQ-to-SQL 编译查询问题(作为未编译查询工作)
【发布时间】:2009-09-14 10:08:37
【问题描述】:

我在 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 的转换。

再一次,如果查询未编译,这将完美运行,只是一个常规的 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 不能与我的扩展方法一起使用。互联网上有关此主题的信息几乎不存在。

【问题讨论】:

  • 我不明白为什么这是社区维基。

标签: c# linq linq-to-sql expression-trees compiled-query


【解决方案1】:

我相信编译后的查询必须可以翻译成 SQL,而你的扩展方法不能。如果您分析由“常规”查询创建的 SQL,您可能会发现它正在选择整个表,因此它可以将所有行提供给您的扩展方法。

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

由于 Skip,OrderBy 也是一个问题。您需要将此转换为 SQL,否则 LINQ 必须返回所有行才能将它们过滤到客户端。

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

编辑:

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

考虑一下这个link,它似乎与您的问题相似。它引用了另一个更详细的link

看起来 MethodCallExpression 是问题所在。

代码有点长,待发 这里,但类似于 tomasp.net 的 扩展器,我访问每个表达式 表达式树,如果节点是 调用一个 MethodCallExpression 返回表达式的方法 树,我替换它 MethodCallExpression 由表达式 调用方法返回的树。

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

【讨论】:

  • 当我创建一个断点时,我可以看到只要不是编译查询,SQL 就可以很容易地完全生成(包括 skip、take、orderby 等)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多