首页 > 解决方案 > 在 EF Core 2 中使用 Contains as (NOT) EXIST 不再适用于 EF Core 3.1

问题描述

从 EF Core 2.2 升级到 EF Core 3.1 后,我遇到了“不再在客户端上评估 LINQ 查询”问题。
我在 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();

在 EF Core 3.1 中,第二个查询在 Contains 方法上失败,并出现“无法翻译”错误。经过反复试验,我将其重写如下:

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));

现在虽然这可行,但这不是我想要的,因为我希望在服务器上执行整个查询。那可能吗?

我可以将第一个查询 (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

标签: c#linqmigrationef-core-3.1

解决方案


尝试使用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();

此外,我非常确定 EF Core 2 之前在内存中执行了此过滤 - 请参阅自动静默客户端评估重大更改

旧行为

在 3.0 之前,当 EF Core 无法将作为查询的一部分的表达式转换为 SQL 或参数时,它会自动评估客户端上的表达式。默认情况下,客户端对可能昂贵的表达式的评估只会触发警告。

新行为

从 3.0 开始,EF Core 仅允许在客户端评估顶级投影(查询中的最后一个 Select() 调用)中的表达式。当查询的任何其他部分中的表达式无法转换为 SQL 或参数时,将引发异常。


推荐阅读