繁体   English   中英

实体框架、LINQ:不能将 Any 与 StartsWith 一起使用?

[英]Entity Framework, LINQ: can't use Any with StartsWith?

我有一个简单的实体框架 LINQ 查询。 目标是找到所有以 A、B 或 C 开头的载体:

var letters = new List<string>() { "A", "B", "C" }; // Dynamic, can be many
var results = db.Carriers.AsNoTracking()
    .Where(c => letters.Any(val => c.Name.StartsWith(val)))
    .ToList();

我得到

System.InvalidOperationException: 'LINQ 表达式 'DbSet .Where(c => __letters_0 .Any(val => val == "" || c.Name != null && val != null && c.Name.StartsWith(val) ))' 无法翻译。 以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。 有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038

没有办法做到这一点吗?

在实体框架 6 中,此查询可以正常运行。 EF6 支持StartsWith SQL 转换,并且支持在查询表达式中使用本地序列( letters )。

EF 核心 3(异常消息所指示的问题中的版本)还支持StartsWith SQL 转换。 这里的问题(另一个答案完全忽略了)是本地序列的使用方式不受支持。 像这样的查询...

var results = db.Carriers.AsNoTracking()
    .Where(c => letters.Contains(c.Name))
    .ToList();

...将得到支持,因为letters可以简单地转换为IN子句。 但当然,这是一个完全不同的查询。

使用letters.Any需要 EF 将letters转换为可以在 SQL 中连接的“东西”。 EF6 通过在 SQL 查询中构建结果集来做到这一点:

    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM  (SELECT 
            N'A' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
        UNION ALL
            SELECT 
            N'B' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]
        UNION ALL
            SELECT 
            N'C' AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
        WHERE ( CAST(CHARINDEX([UnionAll2].[C1], [Extent1].[Name]) AS int)) = 1

哪个有效,但根本无法扩展。 EF core 3 不支持它,并且没有其他答案中建议的简单解决方法。

一种可能的解决方法是使用||构建谓词 (或)谓词,例如:

var pred = letters.Aggregate(PredicateBuilder.False<Carrier>(), 
    (p,x) => p = p.Or(c => c.Name.StartsWith(x)));
var results = db.Carriers.AsNoTracking()
    .Where(pred)
    .ToList();

其中PredicateBuilder是像 Linqkit 或this one这样的谓词构建器。 但是这种方法也不是可扩展的。 EF创建中的每个条目参数letters ,所以你可能会碰到在SQL Server 2100的参数阈值。

实体框架不允许使用自定义谓词。 这就是您的查询失败并显示一条消息的原因:

……无法翻译。 要么以可以翻译的形式重写查询......

因此,这很可能意味着您的 EF 版本要么不支持StartsWith要么存在某些组合,例如无法翻译的StartsWithAny

解决方法可能是:

  1. 首先,您可以使用Contains方法以最接近的方式过滤您的查询。
var results = students.Where(s => letters.Any(l => s.name.Contains(l)));

是我为 .NET fiddle 编写的一个类似示例。


  1. 接下来,如果您仍想使用StartsWith但您的 EF 不支持您可以将查询转换为 IN-MEMORY 集合:
var results = filter_by_contains_query
              .ToList() // convert filtered result to in-memory collection
              .Where(i => letters.Any(l => i.StudentName.StartsWith(l)));

其中filter_by_contains_query是第一部分的查询。


更多的:

  • 关于 EF 支持的方法,请查看此处
  • 关于StartsWithEndsWithContains讨论在这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM