【问题标题】:Using `System.String Concat` in `LINQ to Entities` generates `CAST`s instead of `CONCAT`在 `LINQ to Entities` 中使用 `System.String Concat` 会生成 `CAST`s 而不是 `CONCAT`
【发布时间】:2019-07-30 16:23:38
【问题描述】:

正如我从docs 中了解到的那样,当人们从查询技术(例如 LINQ to Entities)中使用string.Concat 时,这个规范函数应该被转换为正在使用的提供程序的正确对应存储函数,这意味着@ 987654325@ 为MsSQLT-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)

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


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

【问题讨论】:

  • Id 的数据库和 C# 类型是什么?
  • 通过从包含之前的列表项中删除“:”来反转您的 where 子句,它应该更优化。
  • @VidmantasBlazevicius 用作分隔符的分号,为简洁起见,此示例进行了简化,长版本连接多个字段并在列表中查找匹配项。
  • @NetMage 数据库是 MsSQL 2016,Id 列是 int
  • 因此,要与int 进行字符串连接,LINQ 将强制转换为字符串。您可以尝试使用 SqlFunctions.StringConvert 手动强制一个。

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


【解决方案1】:

可以使用 item.Id+":" 我经常用这个

【讨论】:

  • 它将产生完全相同的结果,这正是我开始的地方。
【解决方案2】:

我认为您可以一起从 SQL 中删除串联。似乎您有一个包含数字后跟冒号的字符串列表。为什么不从数字中去掉冒号,直接进行数字比较呢?

var list2 = list.Select(i => int.Parse(i.Replace(':','')));
var items = (from item in testDB.Items
        where list2.Contains(item.Id,":"))
        .AsEnumerable()   // switch from DB query to memory query
        .Select(item => new
        {
           Hit = string.Concat(item.Id,":"),
           Item = item
        }).ToList();

【讨论】:

  • 这种方式是行不通的,因为列表应该包含要与多个列进行比较的值,并且这些值用冒号分隔。也许给定的例子过于简单了......
【解决方案3】:

所以为了让事情顺利进行,我们不会使用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 来完成,但这是解决方法的一般思路。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    • 1970-01-01
    • 2016-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多