Поиск общих предметов оценивается локально - PullRequest
1 голос
/ 05 ноября 2019

Использование 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();

1 Ответ

2 голосов
/ 05 ноября 2019

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) создание вручную предиката на основе Or с классом Expression (сложно и работает для небольшой памяти)коллекций) и (3) замените коллекцию памяти действительными IQueryable<>, например

var userSkills = users
    .Where(x => x.Id == 1)
    .SelectMany(x => x.UserSkills);

и используйте исходный запрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...