查找共同项目在本地进行评估
Finding common items is evaluated locally
使用 Entity Framework Core 2.2 我有以下查询:
IQueryable<User> users = _context.Users.AsNoTracking();
User user = await users
.Include(x => x.UserSkills)
.ThenInclude(x => x.Skill)
.FirstOrDefaultAsync(x => x.Id == 1);
var userSkills = user.UserSkills.ToList();
IQueryable<Lesson> lessons = _context.Lessons.AsNoTracking();
var test = lessons
.Where(x => x.IsEnabled)
.Where(x => x.LessonSkills.All(y => userSkills.Any(z => y.SkillId == z.SkillId)))
.ToList();
我正在寻找包含所有课程技能的用户技能。
当我 运行 这个查询时,我得到以下错误:
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll:
'Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning:
The LINQ expression 'where ([y].SkillId == [z].SkillId)' could not be translated and will be evaluated locally.'.
如何更改查询来解决这个问题?
更新
我需要使用额外选项 (y.SkillLevelId <= z.SkillLevelId
) 扩展此查询:
var test = lessons
.Where(x => x.IsEnabled)
.Where(x => x.LessonSkills.All(y => userSkills.Any(z =>
y.SkillId == z.SkillId
&&
y.SkillLevelId <= z.SkillLevelId)))
.ToList();
userSkills
是内存中的集合,根据我目前使用 EF6 和 EF Core 的经验,我可以说内存中集合的唯一可靠的可翻译构造是原语上的 Enumerable.Contains
方法输入内存中的集合。
所以下面解决的问题就是问题
首先(应该在查询表达式树之外):
var userSkillIds = user.UserSkills.Select(x => x.SkillId);
然后代替
.Where(x => x.LessonSkills.All(y => userSkills.Any(z => y.SkillId == z.SkillId)))
使用等效的(但可翻译):
.Where(x => x.LessonSkills.All(y => userSkillIds.Contains(y.SkillId)))
更新:如果您不能使用Contains
,在 EF Core 开始支持它之前您有的选项是 (1) EntityFrameworkCore.MemoryJoin 包 (我个人还没有测试过,但这个想法很有趣),(2)使用 Expression
class 手动构建基于 Or
的谓词(很难并且适用于小内存集合)和(3 ) 将内存集合替换为真实的IQueryable<>
,例如
var userSkills = users
.Where(x => x.Id == 1)
.SelectMany(x => x.UserSkills);
并使用原始查询。
使用 Entity Framework Core 2.2 我有以下查询:
IQueryable<User> users = _context.Users.AsNoTracking();
User user = await users
.Include(x => x.UserSkills)
.ThenInclude(x => x.Skill)
.FirstOrDefaultAsync(x => x.Id == 1);
var userSkills = user.UserSkills.ToList();
IQueryable<Lesson> lessons = _context.Lessons.AsNoTracking();
var test = lessons
.Where(x => x.IsEnabled)
.Where(x => x.LessonSkills.All(y => userSkills.Any(z => y.SkillId == z.SkillId)))
.ToList();
我正在寻找包含所有课程技能的用户技能。
当我 运行 这个查询时,我得到以下错误:
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll:
'Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning:
The LINQ expression 'where ([y].SkillId == [z].SkillId)' could not be translated and will be evaluated locally.'.
如何更改查询来解决这个问题?
更新
我需要使用额外选项 (y.SkillLevelId <= z.SkillLevelId
) 扩展此查询:
var test = lessons
.Where(x => x.IsEnabled)
.Where(x => x.LessonSkills.All(y => userSkills.Any(z =>
y.SkillId == z.SkillId
&&
y.SkillLevelId <= z.SkillLevelId)))
.ToList();
userSkills
是内存中的集合,根据我目前使用 EF6 和 EF Core 的经验,我可以说内存中集合的唯一可靠的可翻译构造是原语上的 Enumerable.Contains
方法输入内存中的集合。
所以下面解决的问题就是问题
首先(应该在查询表达式树之外):
var userSkillIds = user.UserSkills.Select(x => x.SkillId);
然后代替
.Where(x => x.LessonSkills.All(y => userSkills.Any(z => y.SkillId == z.SkillId)))
使用等效的(但可翻译):
.Where(x => x.LessonSkills.All(y => userSkillIds.Contains(y.SkillId)))
更新:如果您不能使用Contains
,在 EF Core 开始支持它之前您有的选项是 (1) EntityFrameworkCore.MemoryJoin 包 (我个人还没有测试过,但这个想法很有趣),(2)使用 Expression
class 手动构建基于 Or
的谓词(很难并且适用于小内存集合)和(3 ) 将内存集合替换为真实的IQueryable<>
,例如
var userSkills = users
.Where(x => x.Id == 1)
.SelectMany(x => x.UserSkills);
并使用原始查询。