![](/img/trans.png)
[英]StartsWith with any string from list to SQL request with LINQ and Entity Framework
[英]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
要么存在某些组合,例如无法翻译的StartsWith
和Any
。
解决方法可能是:
Contains
方法以最接近的方式过滤您的查询。var results = students.Where(s => letters.Any(l => s.name.Contains(l)));
这是我为 .NET fiddle 编写的一个类似示例。
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
是第一部分的查询。
更多的:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.