首页 > 解决方案 > 在 `LINQ to Entities` 中使用 `System.String Concat` 会生成 `CAST`s 而不是 `CONCAT`

问题描述

正如我从文档中了解到的那样,当使用string.Concat查询技术(例如 LINQ to Entities)时,此规范函数应转换为正在使用的提供程序的正确相应存储函数,即Concat' MsSQLs T-SQL。但是当我运行以下测试代码时:

var items = (from item in testDB.Items
            where list.Contains(string.Concat(item.Id,":"))
            select new
            {
               Hit = string.Concat(item.Id,":"),
               Item = item
            }).ToList();

正在生成以下 SQL:

SELECT 
    [Extent1].[Id] AS [Id], 
     CAST( [Extent1].[Id] AS nvarchar(max)) + N':' AS [C1],
    FROM  [testDB].[Items] AS [Extent1]
    WHERE (CAST([Extent1].[Id] AS nvarchar(max)) + N':' -- N.B.
    IN (N'1:', N'2:', N'3:')) AND (CAST([Extent1].[Id] AS nvarchar(max)) + N':' IS NOT NULL)

注意:(+加号运算符) withCAST而不是Concat正在使用。
显然我做错了什么,但是什么?问题是CASTtoNVARCHAR(MAX)需要大量时间,尤其是在连接多个字段时。


看起来无法做到,因为Concat使用了sql_variant未定义的类型,因此SQLProviderManifest不支持重载,因此只能映射一个函数签名(模式中的名称应该是唯一的),因此显然它们只是通过连接进行了短路在需要时使用带有强制转换的加号运算符,即使我Concat首先映射 in 代码,生成SQL的仍在使用它,所以它变得很明显。我认为唯一的方法是利用DbSet.SqlQuery.

标签: c#entity-frameworklinqentity-framework-6linq-to-entities

解决方案


所以为了让事情正常工作,我们不会使用LINQ,但是DbSet.SqlQuery

var contains = list.Aggregate(new System.Text.StringBuilder(),
                              (sb, s) => sb.Append($"N'{s}', "),
                              sb => (sb.Length > 0)
                                      ? sb.ToString(0, sb.Length - 2)
                                      : null);
if (contains == null)
{
  //ToDo: list is empty, we've got a problem
}
else
{
  var query = testDB.Items.SqlQuery($@"SELECT [E1].[Id],..
                                     FROM [testDB].[Items] AS [E1]
                                     WHERE CONCAT_WS(':', [E1].[Id],..) IN ({contains })");

}

当然应该用SqlParameters 来完成,但这是解决方法的一般思路。


推荐阅读