У меня есть следующий запрос LINQ-to-entities
IQueryable> GetFirstOperationsForEveryId
(IQueryable> 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
is a generic query. It can be obtained in the following way
var history1 = MyEntitiySet1.Select(obj =>
new History{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
new History{ obj.OperationId, GenericId = obj.StringId });
In the end of all I want a generic query being able to work with any entity collection convertible to History
.
Проблема заключается в том, что код не компилируется из-за сравнения GenericId во внутреннем запросе (Operator '==' не может применяться к операндам типов T и T.
Если я изменяю == на h1.GenericId.Equals (h.GenericId)
, я получаю следующий NotSupportedException
:
Невозможно ввести тип «System.Int64» для ввода «System.Object». LINQ to Entities поддерживает только листинг примитивных типов данных Entity Data Model.
Я попытался сделать группировку вместо подзапроса и присоединиться к результатам.
IQueryable> GetFirstOperationsForEveryId
(IQueryable> 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).
Существует возможность построить целое выражение для выражения external where. Но код будет слишком длинным и неясным.
Существуют ли какие-либо простые обходные пути для сравнения равенства с дженериками в LINQ-to-entity?
Попробуйте это, я думаю, что он должен выполнить то, что вы хотите, без дополнительного запроса/присоединения
IQueryable> GetFirstOperationsForEveryId
(IQueryable> 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;
}
IQueryable> GetFirstOperationsForEveryId
(IQueryable> 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;
}
Вы также можете установить общее ограничение на T для IItemHistory inteface, которое реализует свойство GenericId и OperationId.
Мой вопрос уже содержит решение. Второй метод с group + join работает хорошо, если таблица правильно проиндексирована . Для извлечения 370 тыс. Строк из таблицы базы данных требуется 3,28 секунды. На самом деле в не-generic варианте первый запрос медленнее на postgresql, чем второй. 26,68 секунды против 4,75.