Как выполнить групповое объединение в .NET Core 3.0 Entity Framework?

13

С изменениями в .NET Core 3.0 я получаю

... NavigationExpandingExpressionVisitor 'не удалось. Это может указывать либо на ошибку, либо на ограничение в EF Core. См. Https://go.microsoft.com/fwlink/?linkid=2101433 для получения более подробной информации.) ---> System.InvalidOperationException: Обработка выражения LINQ 'GroupJoin, ...

Это действительно простой запрос, поэтому должен быть способ выполнить его в .NET CORE 3.0:

 var queryResults1 = await patients
            .GroupJoin(
                _context.Studies,
                p => p.Id,
                s => s.Patient.Id,
                (p, studies) => new 
                {
                    p.DateOfBirth,
                    p.Id,
                    p.Name,
                    p.Sex,
                   Studies =studies.Select(s1=>s1)
                }
            )
            .AsNoTracking().ToListAsync();

Я в основном ищу запрос Linq (или синтаксис метода, как указано выше), который присоединит Исследования к Пациентам и установит Исследования пустым списком или пустым, если нет исследований для данного пациента.

Любые идеи? Это работало в .NET Core 2.2. Также ссылка MSFT выше упоминает, что изменение взлома ключа связано с оценкой на стороне клиента и избегает того, чтобы сгенерированный запрос считывал целые таблицы, которые затем должны быть присоединены или отфильтрованы на стороне клиента. Однако с этим простым запросом соединение должно быть легко выполнимым на стороне сервера.

shelbypereira
источник

Ответы:

11

Как обсуждалось здесь , вы пытаетесь запроса , который не поддерживается базой данных. EF Core 2 использовал оценку на стороне клиента, чтобы заставить ваш код работать, но EF Core 3 отказывается, потому что удобство на стороне клиента идет за счет трудных для отладки проблем производительности по мере увеличения набора данных.

Вы можете использовать DefaultIfEmptyкоманду «Выйти», чтобы присоединиться к исследованиям пациентов, а затем сгруппировать их вручную ToLookup.

var query =
    from p in db.Patients
    join s in db.Studies on p.Id equals s.PatientId into studies
    from s in studies.DefaultIfEmpty()
    select new { Patient = p, Study = s };

var grouping = query.ToLookup(e => e.Patient); // Grouping done client side

Приведенный выше пример охватывает полные объекты «Пациент» и «Исследование», но вместо этого вы можете выбрать столбцы «Вишня». Если требуемые данные от пациента слишком велики для повторения для каждого исследования, в присоединенном запросе выберите только идентификатор пациента, запрашивая остальные данные пациента в отдельном несвязанном запросе.

Эдвард Брей
источник
2
Ответ работает! Я думаю, что в переводчике запросов еще есть над чем поработать. Простой запрос, подобный этому, должен быть переводимым. Не должно быть проблем с производительностью при простом групповом объединении двух таблиц, так как набор данных увеличивается при условии, что FK / индексы верны. Я подозреваю, что у многих людей возникнет проблема, объединение в две таблицы - довольно стандартный и часто используемый запрос.
Shelbypereira
@ she72 Я согласен. Похоже, что проблема связана с различием в том, как LINQ и SQL используют ключевое слово «group». EF Core должен преобразовать LINQ groupbyв левые соединения, где это не отодвигает больше строк, чем ожидалось. Я разместил комментарий соответственно.
Эдвард Брей
У меня есть дополнительный вопрос, я все еще пытаюсь понять, почему группирование для этого типа запроса должно выполняться на стороне клиента, кажется ограничением новой платформы LINQ. Для случая выше я не вижу никаких рисков, что это замедляет выполнение на стороне клиента неожиданными способами. Вы можете уточнить?
Шелбыперейра
1
И в качестве дальнейшего наблюдения главная проблема заключается в том, чтобы: в вашем переформулированном запросе, на какие группы клиентов я делаю, если у меня 1000 исследований на пациента, я буду загружать каждого пациента 1000 раз из БД? Есть ли альтернатива для принудительного выполнения этой работы в БД и возврата сгруппированных результатов?
Шелбыперейра
1
@ shev72 Единственная группировка, которую понимает база данных, включает агрегаты, например, запрос пациентов с количеством исследований на одного пациента. База данных всегда возвращает прямоугольный набор данных. Иерархическая группировка должна быть составлена ​​клиентом. Вы можете рассматривать это как оценку на стороне клиента или как часть ORM . В иерархической группировке данные родительской сущности повторяются, но не запрашиваются.
Эдвард Брей
0

Была точно такая же проблема и большая борьба с ней. Оказывается, что .net Core 3.0 не поддерживает Join или Groupjoin в синтаксисе метода (пока?). Самое интересное в том, что он работает в синтаксисе запросов.

Попробуйте это, это синтаксис запроса с небольшим количеством синтаксиса метода. Это хорошо переводится в правильный запрос SQL с хорошим внешним левым соединением, и он обрабатывается в базе данных. У меня нет ваших моделей, поэтому вы должны сами проверить синтаксис ....

var queryResults1 = 
    (from p in _context.patients
    from s in _context.Studies.Where(st => st.PatientId == p.Id).DefaultIfEmpty()
    select new
    {
        p.DateOfBirth,
        p.Id,
        p.Name,
        p.Sex,
        Studies = studies.Select(s1 => s1)
    }).ToListAsync();
hwmaat
источник
Кстати, Join и GroupJoin с методом syntac DO работают с неосновными Framework и EF. И сделайте перевод на правильный запрос, который находится на стороне сервера
hwmaat
1
что такое учеба в учебе. Выберите (s1 => s1)
Анкур Арора
Модели не были включены в вопрос, поэтому я не знаю модель исследований. Я думаю, что это виртуальная коллекция в модели.
hwmaat