[英]Entity Framework, LINQ: can't use Any with StartsWith?
I have a simple Entity Framework LINQ query.我有一个简单的实体框架 LINQ 查询。 The goal is to find all the Carriers that start with A, B, or C:
目标是找到所有以 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();
I get我得到
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.
System.InvalidOperationException: 'LINQ 表达式 'DbSet .Where(c => __letters_0 .Any(val => val == "" || c.Name != null && val != null && c.Name.StartsWith(val) ))' 无法翻译。 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().
以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。 See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038 。
Is there no way to do this?没有办法做到这一点吗?
In Entity Framework 6 this query would run fine.在实体框架 6 中,此查询可以正常运行。 EF6 supports SQL translation of
StartsWith
and it supports using local sequences ( letters
) in a query expression. EF6 支持
StartsWith
SQL 转换,并且支持在查询表达式中使用本地序列( letters
)。
EF core 3 (the version in the question as the exception message indicates) also supports SQL translation of StartsWith
. EF 核心 3(异常消息所指示的问题中的版本)还支持
StartsWith
SQL 转换。 The problem here (which the other answer misses completely) is that the local sequence is used in a way that's not supported.这里的问题(另一个答案完全忽略了)是本地序列的使用方式不受支持。 A query like...
像这样的查询...
var results = db.Carriers.AsNoTracking()
.Where(c => letters.Contains(c.Name))
.ToList();
...would be supported because letters
can simply be translated into an IN
clause. ...将得到支持,因为
letters
可以简单地转换为IN
子句。 But of course that's an entirely different query.但当然,这是一个完全不同的查询。
Using letters.Any
requires EF to convert letters
into "something" that can be joined with in SQL.使用
letters.Any
需要 EF 将letters
转换为可以在 SQL 中连接的“东西”。 EF6 does this by building a result set in the SQL query: 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
Which works, but isn't scalable at all.哪个有效,但根本无法扩展。 EF core 3 doesn't support it and there's no easy work-around as suggested in the other answer.
EF core 3 不支持它,并且没有其他答案中建议的简单解决方法。
A possible work-around is to build a predicate with ||
一种可能的解决方法是使用
||
构建谓词(Or) predicates, for example: (或)谓词,例如:
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();
Where PredicateBuilder
is a predicate builder like Linqkit or this one .其中
PredicateBuilder
是像 Linqkit 或this one这样的谓词构建器。 But this method isn't scalable either.但是这种方法也不是可扩展的。 EF creates a parameter for each entry in
letters
, so you may hit the 2100-parameters threshold in Sql Server. EF创建中的每个条目参数
letters
,所以你可能会碰到在SQL Server 2100的参数阈值。
The Entity Framework doesn't allow usage of custom predicates.实体框架不允许使用自定义谓词。 That's the reason why your query is failing with a message:
这就是您的查询失败并显示一条消息的原因:
... could not be translated.
……无法翻译。 Either rewrite the query in a form that can be translated ...
要么以可以翻译的形式重写查询......
So, this most probably means that your EF version either doesn't support StartsWith
or there is some combination, like StartsWith
and Any
that can't be translated.因此,这很可能意味着您的 EF 版本要么不支持
StartsWith
要么存在某些组合,例如无法翻译的StartsWith
和Any
。
A workaround could be:解决方法可能是:
Contains
method.Contains
方法以最接近的方式过滤您的查询。var results = students.Where(s => letters.Any(l => s.name.Contains(l)));
Here is a similar example I wrote for .NET fiddle.这是我为 .NET fiddle 编写的一个类似示例。
StartsWith
but your EF doesn't support that you can convert your query to IN-MEMORY collection :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)));
where filter_by_contains_query
is a query from the 1st part.其中
filter_by_contains_query
是第一部分的查询。
More:更多的:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.