查找共同项目在本地进行评估

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

并使用原始查询。