ЛЮБОЙ со ВСЕМ в Entity Framework оценивает локально - PullRequest
1 голос
/ 10 мая 2019

У меня есть следующий запрос Entity Framework 2.0:

var user = context.Users.AsNoTracking()
  .Include(x => x.UserSkills).ThenInclude(x => x.Skill)
  .Include(x => x.UserSkills).ThenInclude(x => x.SkillLevel)
  .FirstOrDefault(x => x.Id == userId);

var userSkills = user.UserSkills.Select(z => new { 
  SkillId = z.SkillId, 
  SkillLevelId = z.SkillLevelId 
}).ToList()

Затем я попытался следующий запрос:

var lessons = _context.Lessons.AsNoTracking()
  .Where(x => x.LessonSkills.All(y => 
     userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
  .ToList();

Этот запрос оценивается локально, и я получаю сообщение:

The LINQ expression 'where (([y].SkillId == [z].SkillId) AndAlso ([y].SkillLevelId <= [z].SkillLevelId))' could not be translated and will be evaluated locally.'.

Я пытался решить ее, используя userSkills вместо user.UserSkills, но безуспешно.

Есть ли способ выполнить этот запрос на сервере?

Ответы [ 2 ]

1 голос
/ 10 мая 2019

Вы должны попытаться ограничить использование коллекций в памяти внутри запросов LINQ to Entities, в основном, Contains для сбора примитивных значений, который в настоящее время является единственной переводимой конструкцией сервера.

Поскольку Contains не являетсяПрименимо здесь, вы не должны использовать коллекцию памяти, но соответствующий подзапрос на стороне сервера:

var userSkills = context.UserSkills
    .Where(x => x.UserId == userId);

var lessons = context.Lessons.AsNoTracking()
  .Where(x => x.LessonSkills.All(y =>
     userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
  .ToList();

или даже встроить первый подзапрос в основной запрос:

var lessons = context.Lessons.AsNoTracking()
  .Where(x => x.LessonSkills.All(y =>
     context.UserSkills.Any(z => z.UserId == userId && y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
  .ToList();
0 голосов
/ 10 мая 2019

Используйте Contains на сервере, а затем отфильтруйте на клиенте:

var userSkillIds = userSkills.Select(s => s.SkillId).ToList();

var lessons = _context.Lessons.AsNoTracking()
                              .Where(lsn => lsn.LessonSkills.All(lsnskill => userSkillIds.Contains(lsnskill.SkillId)))
                              .AsEnumerable() // depending on EF Core translation, may not be needed
                              .Where(lsn => lsn.LessonSkills.All(lsnskill => userSkills.Any(uskill => uskill.SkillId == lsnskill.SkillId && lsnskill.SkillLevelId <= uskill.SkillLevelId)))
                              .ToList();
...