I have a following LINQ-to-entities query
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
is a generic query. It can be obtained in the following way
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 });
In the end of all I want a generic query being able to work with any entity collection convertible to History<T>
.
The problem is the code does not compile because of GenericId comparison in the inner query (Operator '==' cannot be applied to operands of type 'T' and 'T').
If I change == to h1.GenericId.Equals(h.GenericId)
I get the following NotSupportedException
:
Unable to cast the type 'System.Int64' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.
I've tried to do grouping instead of subquery and join the results.
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;
}
It compiles because GenericId's are compared with equals
keyword and it works but the query with real data is too slow (it has been running for 11 hours on dedicated postgresql server).
There is an option to build a whole expression for the outer where statement. But the code would be too long and unclear.
Are there any simple workarounds for equality comparison with generics in LINQ-to-entities?
Try this, I think it should accomplish what you want without the extra query/join
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;
}
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;
}
您还可以在I上为实现GenericId和OperationId属性的IItemHistory接口设置通用约束。
My question already contains a solution. The second method with group + join works well if the table is properly indexed . It takes 3.28 seconds to retrieve 370k rows from the database table. In fact in non-generic variant the first query is slower on postgresql than the second one. 26.68 seconds vs 4.75.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.