我有一个简单的 Entity Framework 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: 'The LINQ expression 'DbSet .Where(c => __letters_0 .Any(val => val == "" || c.Name != null && val != null && c.Name.StartsWith(val)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
没有办法做到这一点吗?
最佳答案
在 Entity Framework 6 中,此查询可以正常运行。 EF6 支持 StartsWith
的 SQL 转换并且它支持在查询表达式中使用本地序列( letters
)。
EF core 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 个参数的阈值。
关于c# - Entity Framework ,LINQ : can't use Any with StartsWith?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63732025/