我目前正在开发一个 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 的情况下实现此要求?任何见解或建议将不胜感激。
首先定义公共分组键类:
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();
}
}
其余代码应该按预期工作。