在 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.
从 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.