在 C# 中使用附加列按时间段对 IQueryable 进行分组

问题描述 投票:0回答:1

我目前正在开发一个 C# 项目,我需要根据日期列按不同时间段(例如每年、每季度、每月、每周、每天)对 IQueryable 进行分组。为了实现这一点,我创建了一个辅助函数 GroupQueryByTimePeriod。但是,我还需要在分组中包含其他列,例如此特定查询或AssignedTo 中的 ClientId,以及基于各自列的其他查询中。

我尝试修改函数以将这些附加列包含在分组中,但遇到了困难。动态 LINQ 似乎是一个潜在的解决方案,但不幸的是,它无法调用 SQL 用户定义函数 (UDF),并且我需要使用 UDF (GetWeekNumber) 计算准确的周数。简单地将天数除以 7 无法提供我所需的精度。

这是我当前代码的简化版本:

public static IQueryable<IGrouping<object, T>> GroupQueryByTimePeriod<T>(this IQueryable<T> query,
    TimePeriodGroupingType groupingType,
    ApplicationDbContext context
) where T : IDate
{
    IQueryable<IGrouping<object, T>> groupedQuery = null;
    switch (groupingType)
    {
        case TimePeriodGroupingType.Annually:
            groupedQuery = query.GroupBy(x => new
            {
                x.Date.Year,
            }).AsQueryable();
            break;
        case TimePeriodGroupingType.Quarterly:
            groupedQuery = query.GroupBy(x => new
            {
                x.Date.Year,
                Quarter = ((x.Date.Month - 1) / 3)
            }).AsQueryable();
            break;
        case TimePeriodGroupingType.Monthly:
            groupedQuery = query.GroupBy(x => new
            {
                x.Date.Year,
                //Quarter = ((x.Date.Month - 1) / 3),
                x.Date.Month
            }).AsQueryable();
            break;
        case TimePeriodGroupingType.Weekly:
            // Assuming weeks start on Mondays
            groupedQuery = query.GroupBy(x => new
            {
                x.Date.Year,
                //Quarter = ((x.Date.Month - 1) / 3),
                //x.Date.Month,
                Week = context.GetWeekNumber(x.Date)
            }).AsQueryable();
            break;
        case TimePeriodGroupingType.Daily:
            groupedQuery = query.GroupBy(x => new
            {
                x.Date.Year,
                //Quarter = ((x.Date.Month - 1) / 3),
                //x.Date.Month,
                //Week = context.GetWeekNumber(x.Date),
                Date = x.Date.Date,
            }).AsQueryable();
            break;
    }
    return groupedQuery;
}

日期列界面

public interface IDate
{
    DateTime Date { get; }
}

使用示例:


private async Task<List<T>> GetPaymentChartReportData<T>(PaymentChartSearchViewModel search) where T : PaymentReportViewModel, IDate, ILabel, new()
{
    var queryablePayments = (from p in _db.Payments
                             join c in _db.Clients on p.ClientId equals c.Id
                             join cu in _db.Users on c.UserId equals cu.Id
                             join au in _db.Users on c.AssignedToUserId equals au.Id
                             where p.Type == (int)PaymentTypeCatalog.Payment
                             && (search.FromDate == null || search.FromDate <= p.Date)
                             && (search.ToDate == null || search.ToDate >= p.Date)
                             select new T
                             {
                                 Client = new ClientBriefViewModel
                                 {
                                     Id = c.Id,
                                     FirstName = cu.FirstName,
                                     LastName = cu.LastName,
                                     Username = cu.UserName
                                 },
                                 AssignedTo = new EmployeeBriefViewModel()
                                 {
                                     Id = au.Id,
                                     FirstName = au.FirstName,
                                     LastName = au.LastName,
                                 },
                                 Amount = p.Amount,
                                 Date = p.Date
                             }).AsNoTracking();
    var queryableTimePeriodGroupedPaymentsQueryable = queryablePayments
    .GroupQueryByTimePeriod(search.TimePeriodGroupingType, _db);
    var queryableTimePeriodGroupedPayments = queryableTimePeriodGroupedPaymentsQueryable.Select(x => new T
    {
        Client = new ClientBriefViewModel
        {
            Id = x.Max(y => y.Client.Id),
            FirstName = x.Max(y => y.Client.FirstName),
            LastName = x.Max(y => y.Client.LastName),
            Username = x.Max(y => y.Client.Username)
        },
        AssignedTo = new EmployeeBriefViewModel()
        {
            Id = x.Max(y => y.AssignedTo.Id),
            FirstName = x.Max(y => y.AssignedTo.FirstName),
            LastName = x.Max(y => y.AssignedTo.LastName),
        },
        Amount = x.Sum(y => y.Amount),
        Date = x.Max(y => y.Date),
    });





    var query = queryableTimePeriodGroupedPayments.ToQueryString();
    var payments = await queryableTimePeriodGroupedPayments.ToListAsync();
    var paymentsReportData = payments.SetLabelAndFillMissingData(search.FromDate, search.ToDate, search.TimePeriodGroupingType);
    return paymentsReportData;
}

分组TimePeriod函数调用

var queryableTimePeriodGroupedPaymentsQueryable = queryablePayments
    .GroupQueryByTimePeriod(search.TimePeriodGroupingType, _db);

我正在寻求有关如何修改 GroupQueryByTimePeriod 函数以适应按附加列(例如,客户信息或成员信息)以及时间段进行分组的指导。 是否有替代方法或解决方法可以让我在不依赖动态 LINQ 的情况下实现此要求?任何见解或建议将不胜感激。

linq entity-framework-core dynamic-linq
1个回答
0
投票

首先定义公共分组键类:

class GroupingKey
{
    public int Year { get; set; }
    public int Month { get; set; }
    public int Quarter { get; set; }
    public int Week { get; set; }
    public DateTime Date { get; set; }
}

然后在分组查询中重用此类:

public static IQueryable<IGrouping<GroupingKey, T>> GroupQueryByTimePeriod<T>(this IQueryable<T> query,
    TimePeriodGroupingType groupingType,
    ApplicationDbContext context
) where T : IDate
{
    switch (groupingType)
    {
        case TimePeriodGroupingType.Annually:
            return query.GroupBy(x => new GroupingKey { Year = x.Date.Year });
        case TimePeriodGroupingType.Quarterly:
            return query.GroupBy(x => new GroupingKey { Year = x.Date.Year, Quarter = Quarter = ((x.Date.Month - 1) / 3) });
        case TimePeriodGroupingType.Monthly:
            return query.GroupBy(x => new GroupingKey { Year = x.Date.Year, Month = x.Date.Month });
        case TimePeriodGroupingType.Weekly:
            // Assuming weeks start on Mondays
            return query.GroupBy(x => new GroupingKey { Year = x.Date.Year, Week = context.GetWeekNumber(x.Date) });
        case TimePeriodGroupingType.Daily:
            return query.GroupBy(x => new GroupingKey { Date = x.Date.Date });
        default:
            throw new InvalidOperationException();
    }
}

其余代码应该按预期工作。

© www.soinside.com 2019 - 2024. All rights reserved.