简体   繁体   English

在 EF Core 2 中使用 Contains as (NOT) EXIST 不再适用于 EF Core 3.1

[英]Using Contains as (NOT) EXIST in EF Core 2 no longer works in EF Core 3.1

After upgrading from EF Core 2.2 to EF Core 3.1 I run into the "LINQ queries are no longer evaluated on the client" issue.从 EF Core 2.2 升级到 EF Core 3.1 后,我遇到了“不再在客户端上评估 LINQ 查询”问题。
I have the below queries in 2.2 which work fine:我在 2.2 中有以下查询,它们工作正常:

var entQry = await
   (from up in _dbContext.Profiles
    join pa in _dbContext.Access
    on up.ProfileId equals pa.ProfileId
    where (up.IdentityUserId == identityUser.Id)
    select new
    {
        pa.LibraryId
    }).ToListAsync();

var libQry = await
   (from en in _dbContext.Entities
    join pa in _dbContext.Access
    on en.LibraryId equals pa.ObjectId
    where (up.IdentityUserId == identityUser.Id
           && !entQry.Contains(new { en.LibraryId }))
    select new
    {
        Id = en.Id
    }).ToListAsync();

In EF Core 3.1 the second query fails on the Contains method with the "cannot translate" error.在 EF Core 3.1 中,第二个查询在 Contains 方法上失败,并出现“无法翻译”错误。 After some trial and error I have rewritten this as follows:经过反复试验,我将其重写如下:

var libQry2 = await
       (from en in _dbContext.Entities
        join pa in _dbContext.Access
        on en.LibraryId equals pa.ObjectId
        where (up.IdentityUserId == identityUser.Id)
        select new
        {
            Id = en.Id
        }).ToListAsync();

var libQry = libQry2.Where(w => !entQry.Any(c => c.LibraryId == w.Id));

Now although this works this is not what I want because I want the entire query to be executed on the server.现在虽然这可行,但这不是我想要的,因为我希望在服务器上执行整个查询。 Is that possible?那可能吗?

Can I get the first query (entQry) as a sub query in the second query so that it translates to SQL as:我可以将第一个查询 (entQry) 作为第二个查询中的子查询,以便它转换为 SQL 为:

SELECT en.Id
 FROM Entities en JOIN Access pa ON en.LibraryId equals pa.ObjectId
WHERE x.id NOT IN (SELECT up.LibraryId FROM Profiles up JOIN Access pa ON up.ProfileId = pa.ProfileId)
  AND up.IdentityUserId == @identityUser.Id

Try using Contains with collection of "primitive" types:尝试使用Contains “原始”类型的集合:

var entIds = await
   (from up in _dbContext.Profiles
    join pa in _dbContext.Access
    on up.ProfileId equals pa.ProfileId
    where (up.IdentityUserId == identityUser.Id)
    select pa.LibraryId)
    .ToListAsync(); 

var libQry = await
   (from en in _dbContext.Entities
    join pa in _dbContext.Access
    on en.LibraryId equals pa.ObjectId
    where (up.IdentityUserId == identityUser.Id
           && !entIds.Contains(en.LibraryId))
    select new
    {
        Id = en.Id
    }).ToListAsync();

Also I'm pretty much sure that EF Core 2 previously executed this filtering in-memory - see the automatic silent client side evaluation breaking change :此外,我非常确定 EF Core 2 之前在内存中执行了此过滤 - 请参阅自动静默客户端评估重大更改

Old behavior旧行为

Before 3.0, when EF Core couldn't convert an expression that was part of a query to either SQL or a parameter, it automatically evaluated the expression on the client.在 3.0 之前,当 EF Core 无法将作为查询一部分的表达式转换为 SQL 或参数时,它会自动在客户端上计算表达式。 By default, client evaluation of potentially expensive expressions only triggered a warning.默认情况下,客户端对可能昂贵的表达式的评估只会触发警告。

New behavior新行为

Starting with 3.0, EF Core only allows expressions in the top-level projection (the last Select() call in the query) to be evaluated on the client.从 3.0 开始,EF Core 仅允许在客户端评估顶级投影(查询中的最后一个 Select() 调用)中的表达式。 When expressions in any other part of the query can't be converted to either SQL or a parameter, an exception is thrown.当查询的任何其他部分中的表达式无法转换为 SQL 或参数时,将引发异常。

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

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