EFCore 2.2 Group by Sum和DateDiff

时间:2019-02-13 12:53:03

标签: linq group-by linq-to-entities datediff ef-core-2.2

我正在尝试将以下SQL转换为EF Core查询,并且收到警告,即将在本地评估GroupBy和Sum。无论如何,当前是否有写它可以完全转换为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

这些是我使用过的EF查询:

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);

1 个答案:

答案 0 :(得分:4)

首先,EF Core仍然不支持翻译TimeSpan操作,并且DateTime的差异会产生TimeSpan,因此EF.Functions.DateDiff方法是正确的方法。

第二,它仍然只能在简单的成员访问器表达式上转换GroupBy聚合。因此,您必须预先Select GroupBy表达式:

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)
    });

或使用GroupBy重载允许预先选择聚集的来源:

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)
    });