在 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

从 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

尝试将 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 之前在内存中执行过此过滤 - 请参阅自动静默客户端评估 breaking change:

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. 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. When expressions in any other part of the query can't be converted to either SQL or a parameter, an exception is thrown.