I have a group data in my database like that:
Code ModificationDate
--------------------------
A 2020/01/02
A 2020/01/01
B 2020/01/03
B 2020/01/01
C 2020/01/04
C 2020/01/01
And I want to get the value with the most recent ModificationDate
from each group of codes.
I tried some linq queries, but I always get the same error
Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable'
To workaround it, I wrote this code:
var query = _context.Customers.FromSqlRaw(@"
SELECT t.* FROM(
SELECT[Code], MAX([ModificationDate]) as [ModificationDate]
FROM[Customers]
GROUP BY[Code]
) c
INNER JOIN[Customers] t
ON t.[Code] = c.[Code] AND t.[ModificationDate] = c.[ModificationDate]");
It works fine, but I want to translate it to a linq query that doesn't trigger the mentioned error.
I don't want to use AsEnumerable
or ToList
because I have a lot of data and load it into memory is going to be slow.
This exception occurs frequently with GroupBy
in EF core 3/5. In your case there is way to make EF happy by following this query pattern (based on the Chinook sample database, not knowing your class model):
from c in Customers
from i in c.Invoices.OrderByDescending(i => i.InvoiceDate).Take(1)
select new
{
c.LastName,
i.BillingAddress
}
This is even the preferred query pattern, even failing sufficient GroupBy
support, because (at least in Sql Server, maybe other providers too), EF translates Take
into the efficient ROW_NUMBER() OVER(PARTITION BY ...)
construct in SQL. The LINQ query above is translated to this SQL statement in EF-core 3.1.10:
SELECT [c].[LastName], [t0].[BillingAddress]
FROM [Customer] AS [c]
INNER JOIN (
SELECT [t].[InvoiceId], [t].[BillingAddress], [t].[BillingCity], [t].[BillingCountry], [t].[BillingPostalCode], [t].[BillingState], [t].[CustomerId], [t].[InvoiceDate], [t].[Total]
FROM (
SELECT [i].[InvoiceId], [i].[BillingAddress], [i].[BillingCity], [i].[BillingCountry], [i].[BillingPostalCode], [i].[BillingState], [i].[CustomerId], [i].[InvoiceDate], [i].[Total]
, ROW_NUMBER() OVER(PARTITION BY [i].[CustomerId] ORDER BY [i].[InvoiceDate] DESC) AS [row]
FROM [Invoice] AS [i]
) AS [t]
WHERE [t].[row] <= 1
) AS [t0] ON [c].[CustomerId] = [t0].[CustomerId]
It's a bit disappointing, though, that EF doesn't reduce the fields in the subquery according to the properties requested in the LINQ query. That could be achieved by a query like this:
from c in Customers
from i in c.Invoices.OrderByDescending(i => i.InvoiceDate)
.Select(i => new { i.BillingAddress, i.BillingCity })
.Take(1)
select new
{
c.LastName,
i.BillingAddress,
i.BillingCity
}
...unfortunately causing some code repetition.
Finally i'm using this:
IQueryable<Customer> query =
from c in _context.Customers.GroupBy(a => a.Code).Select(a => a.Max(a => a.ModificationDate))
from s in _context.Customers
where c == s.ModificationDate
select s;
This works fine
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.