I'm trying to translate the following SQL in to an EF Core query and I'm getting warnings that GroupBy and Sum will be evaluated locally. Is there anyway currently to write this that it will fully translate to SQL?
SELECT UserId, ST.StatusId, SUM(DATEDIFF(MINUTE, StartDate, ISNULL(EndDate,GETDATE()))) AS Time
FROM StatusTransaction ST
WHERE
TeamManagerId = 1
AND StartDate >= N'01-01-2019'
AND ISNULL(EndDate,GETDATE()) <= N'01-02-2019'
GROUP BY UserId, ST.StatusId
ORDER BY UserId
And these are the EF queries I've used:
var efFunction = await context
.Where(st => st.TeamManagerId == tmId && st.StartDate >= dateFrom && (st.EndDate ?? DateTime.Now) <= dateTo)
.GroupBy(st => new { st.UserId, st.StatusId })
.Select(g => new
{
g.Key.UserId,
g.Key.StatusId,
Time = g.Sum(st => Microsoft.EntityFrameworkCore.EF.Functions.DateDiffMinute(st.StartDate, st.EndDate)) // null check not done on end date - (st.EndDate ?? DateTime.Now) causes an error here
}).ToListAsync(cancellationToken).ConfigureAwait(false);
var simpleDateSubtraction = await context
.Where(st => st.TeamManagerId == tmId && st.StartDate >= dateFrom && (st.EndDate ?? DateTime.Now) <= dateTo)
.GroupBy(st => new { st.UserId, st.StatusId })
.Select(g => new
{
g.Key.UserId,
g.Key.StatusId,
Time = g.Sum(st => st.EndDate.Value.Subtract(st.StartDate).Minutes)// null check not done on end date - (st.EndDate ?? DateTime.Now) causes an error here
}).ToListAsync(cancellationToken).ConfigureAwait(false);
var groupBySimpleSum = await context
.Where(st => st.TeamManagerId == tmId)
.GroupBy(st => new { st.TeamManagerId, st.OperationsManagerId })
.Select(g => new
{
g.Key.OperationsManagerId,
g.Key.TeamManagerId,
Foo = g.Sum(st => st.UserId) // nonsense but a simple column to sum, this translates fully to SQL
}).ToListAsync(cancellationToken).ConfigureAwait(false);
Before version 2.1, in EF Core the GroupBy LINQ operator would always be evaluated in memory. We now support translating it to the SQL GROUP BY clause in most common cases. There is nothing you can do in previous EF Core versions.
By default, EF Core will log a warning when client evaluation is performed. See here .Try to use RawSQL if it is inefficient. Show activity on this post. If possible, upgrade to EF Core 2.1 (or 2.2) in order to get improved LINQ GroupBy translation. Before version 2.1, in EF Core the GroupBy LINQ operator would always be evaluated in memory.
EF Core 2.2 is trying to bring all the data locally. **Here is the query getting generated** SELECT [adjDetail]. [QuotaId], [adjDetail].
You would use EntityFunctions or DbFunctions DiffDays method. Using DbFunctions, accessed via EF.Functions, you can call DateDiffDay: var ans = from t in Table1 group t by 1 into tg select tg.Sum (r => EF.Functions.DateDiffDay (r.FromDate, r.ToDate)); My SQL to LINQ Recipe might help you with some translation issues in the future.
First, EF Core still doesn't support translating TimeSpan
operations, and DateTime
difference produces TimeSpan
, hence EF.Functions.DateDiff
methods are the right way to go.
Second, it still can translate GroupBy
aggregates only on simple member accessor expressions. So you have to either pre Select
the GroupBy
expressions:
var query = context
.Where(st => st.TeamManagerId == tmId
&& st.StartDate >= dateFrom
&& (st.EndDate ?? DateTime.Now) <= dateTo
)
.Select(st => new
{
st.UserId,
st.StatusId,
Time = EF.Functions.DateDiffMinute(st.StartDate, st.EndDate ?? DateTime.Now)
})
.GroupBy(st => new { st.UserId, st.StatusId })
.Select(g => new
{
g.Key.UserId,
g.Key.StatusId,
Time = g.Sum(st => st.Time)
});
or use the GroupBy
overload which allows pre selecting the source for the aggregates:
var query = context
.Where(st => st.TeamManagerId == tmId
&& st.StartDate >= dateFrom
&& (st.EndDate ?? DateTime.Now) <= dateTo
)
.GroupBy(st => new { st.UserId, st.StatusId }, st => new
{
Time = EF.Functions.DateDiffMinute(st.StartDate, st.EndDate ?? DateTime.Now)
})
.Select(g => new
{
g.Key.UserId,
g.Key.StatusId,
Time = g.Sum(st => st.Time)
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With