Использование Включить с Intersect / Union / Exclude в Linq - PullRequest
0 голосов
/ 10 ноября 2018

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

То, что я пытаюсь сделать, это собрать воедино запрос Linq и затем вызвать .Include(), чтобы получить значения из нескольких дочерних объектов. Например, допустим, у меня есть следующие объекты:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Location { get; set; }
    public ISet<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public Parent Parent { get; set; }
    public string Name { get; set; }
}

И скажем, я хочу выполнить запрос для извлечения записей из Parent, где Name - это какое-то значение, а Location - это другое значение, а затем включить Child записей. Но по какой-то причине я не знаю значения запросов для Name и Location одновременно, поэтому мне нужно взять две отдельные запрашиваемые формы и соединить их, например:

MyDbContext C = new MyDbContext();
var queryOne = C.Parent.Where(p => p.Name == myName);
var queryTwo = C.Parent.Where(p => p.Location == myLocation);
var finalQuery = queryOne.Intersect(queryTwo);

Это прекрасно работает, дает результаты точно так же, как если бы я только что сделал:

var query = C.Parent.Where(p => p.Name == myName && p.Location = myLocation);

И точно так же я могу:

var finalQuery = queryOne.Union(queryTwo);

Чтобы дать мне результаты, как если бы у меня было:

var query = C.Parent.Where(p => p.Name == myName || p.Location = myLocation);

Однако, что я не могу сделать после применения Intersect() или Union(), так это перейти к отображению Child с использованием Include(), например:

finalQuery.Include(p => p.Children);

Этот код скомпилируется, но выдает следующие результаты:

  1. В случае Union() будет создан набор результатов, но никакие Child сущности не будут перечислены.
  2. В случае Intersect() генерируется ошибка времени выполнения при попытке применить Include(), как показано ниже:

Выражение типа 'System.Collections.Generic.IEnumerable`1 [Microsoft.EntityFrameworkCore.Query.Internal.AnonymousObject] нельзя использовать для параметра типа 'System.Collections.Generic.IEnumerable`1 [System.Object]' метода «System.Collections.Generic.IEnumerable`1 [System.Object] Intersect [Объект] (System.Collections.Generic.IEnumerable`1 [System.Object], System.Collections.Generic.IEnumerable`1 [System.Object]) '

Меня смущает то, что этот код будет работать именно так, как и ожидалось:

var query = C.Parent.Where(p => p.Name == myName).Where(p => p.Location == myLocation);
query.Include(p => p.Children);

То есть, с желаемыми результатами, включая перечисленные Child сущности.

1 Ответ

0 голосов
/ 10 ноября 2018

моя методология, возможно, просто выходит за рамки возможностей Linq

Проблема не в LINQ, а в переводе запросов EF Core, и, в частности, в отсутствии метода перевода SQL Intersect / Union / Concat / Except, отслеживаемого # 6812 Запрос: Translate IQueryable .Concat / Union / Intersect / за исключением / и т.д.. на сервер .

Вскоре такие запросы в настоящее время используют оценку клиента , что в сочетании с тем, как EF Core обрабатывает Include, приводит к множеству неожиданных исключений во время выполнения (например, ваш случай # 2) или неправильному поведению (например, * 1016). * Игнорируется Включает в вашем случае # 1).

Таким образом, хотя ваш подход технически имеет смысл, согласно ответу руководителя команды EF Core

Изменение этого параметра на создание одного SQL-запроса на сервере в настоящее время не является главным приоритетом

, так что в настоящее время это даже не запланировано на выпуск 3.0, хотя есть планы изменить (переписать) весь конвейер преобразования запросов, что также может позволить реализовать это.

Пока у вас нет вариантов. Вы можете попробовать обработать деревья выражений запроса самостоятельно, но это сложная задача, и вы, вероятно, поймете, почему она еще не реализована :) Если вы можете преобразовать свои запросы в эквивалентный одиночный запрос с комбинированным условием Where, тогда примените Include будет хорошо.


P.S. Обратите внимание, что даже сейчас ваш подход технически «работает» без Include, предварительная оценка с точки зрения оценки на стороне клиента делает его абсолютно не эквивалентным соответствующему отдельному запросу.

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