Универсальный LINQ-to-entity == Обходной путь - PullRequest
2 голосов
/ 27 октября 2011

У меня следующий запрос LINQ-to-entity

IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
{
    var q = (from h in ItemHistory
             where h.OperationId ==
                (from h1 in ItemHistory
                 where h1.GenericId == h.GenericId
                 select h1.OperationId).Min()
             select h);
    return q;
}

ItemHistory - это общий запрос.Его можно получить следующим образом:

var history1 = MyEntitiySet1.Select(obj =>
    new History<long>{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
    new History<string>{ obj.OperationId, GenericId = obj.StringId });

В конце концов я хочу, чтобы универсальный запрос мог работать с любой коллекцией сущностей, конвертируемой в History<T>.

.код не компилируется из-за сравнения GenericId во внутреннем запросе (оператор '==' не может быть применен к операндам типа 'T' и 'T').

Если я изменяю== до h1.GenericId.Equals(h.GenericId) Я получаю следующее NotSupportedException:

Невозможно привести тип 'System.Int64' к типу 'System.Object'.LINQ to Entities поддерживает только приведение типов примитивов Entity Data Model.

Я попытался выполнить группировку вместо подзапроса и объединить результаты.

IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
{
    var grouped = (from h1 in ItemHistory
                   group h1 by h1.GenericId into tt
                   select new
                   {
                        GenericId = tt.Key,
                        OperationId = tt.Min(ttt => ttt.OperationId)
                   });

    var q = (from h in ItemHistory
             join g in grouped
                on new { h.OperationId, h.GenericId }
                equals new { g.OperationId, g.GenericId }
             select h);
    return q;
}

Он компилируется, потому что GenericIdпо сравнению с ключевым словом equals, и он работает, но запрос с реальными данными слишком медленный (он выполнялся в течение 11 часов на выделенном сервере postgresql).

Существует возможность построить целое выражение для внешнегогде заявление.Но код был бы слишком длинным и неясным.

Есть ли какие-нибудь простые обходные пути для сравнения на равенство с обобщениями в LINQ-to-entity?

Ответы [ 4 ]

1 голос
/ 27 октября 2011

Попробуйте, я думаю, что это должно выполнить то, что вы хотите без дополнительного запроса / соединения

IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
{
  var q = from h in ItemHistory
          group h by h.GenericId into tt
          let first = (from t in tt
                        orderby t.GenericId
                        select t).FirstOrDefault()
          select first;

  return q;
}
0 голосов
/ 28 октября 2011

Мой вопрос уже содержит решение. Второй метод с group + join работает хорошо, если таблица правильно проиндексирована . Извлечение 370 тыс. Строк из таблицы базы данных занимает 3,28 секунды. Фактически в неуниверсальном варианте первый запрос на postgresql медленнее, чем второй. 26,68 секунды против 4,75.

0 голосов
/ 28 октября 2011

Вы также можете установить общее ограничение на T для интерфейса IItemHistory, который реализует свойства GenericId и OperationId.

0 голосов
/ 27 октября 2011
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var grouped = (from h1 in ItemHistory
               group t by h1.GenericId into tt
               select new
               {
                    GenericId = tt.Key,
                    OperationId = tt.Min(ttt => ttt.OperationId)
               });

var q = (from h in ItemHistory
         join g in grouped
            on new { h.OperationId, h.GenericId }
            equals new { g.OperationId, g.GenericId }
         select h);
return q;
}
...