创建一个Linq to实体IQueryable扩展名以进行日期分组(GroupBy)

时间:2018-08-01 09:56:07

标签: c# linq linq-to-entities linq-expressions

我正在尝试为按日期分组的自定义构建表达式树,对此进行转换:

groupedData = entity.GroupBy(e => new DateTime(e.created_date.Year, 1, 1));

进入一个无关源实体的扩展名。到目前为止,我有:

public static IQueryable<IGrouping<DateTime, TSource>> DateGroup<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, DateTime>> dateSelector, GraphDataTimeSpan span)
    {


        var year = Expression.Property(dateSelector.Body, nameof(DateTime.Year));

        var ctorinfo = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) });
        var ctroExp = Expression.New(ctorinfo, year, Expression.Constant(1), Expression.Constant(1));

        var lambda = Expression.Lambda<Func<TSource, DateTime>>(ctroExp, dateSelector.Parameters);

        switch (span)
        {
            case GraphDataTimeSpan.Yearly:

                return source.GroupBy(lambda);

            case GraphDataTimeSpan.Monthly:

                break;
            case GraphDataTimeSpan.Weekly:

                break;
            case GraphDataTimeSpan.Daily:

                break;
        }


        return source.GroupBy(lambda);
    }

但是它根本不起作用,抛出一个错误:

error

虽然我在C#交互式控制台上尝试时看起来还可以:

Expression.Lambda>(ctroExp,dateSelector.Parameters)

  

[test =>新的DateTime(test.Created.Year,1,1)] {正文= [新的DateTime(test.Created.Year,1,1)]

2 个答案:

答案 0 :(得分:1)

我已将您的代码简化为:

public static IQueryable<IGrouping<DateTime, TSource>> DateGroup<TSource>(this IQueryable<TSource> source, Func<TSource, DateTime> dateSelector, GraphDataTimeSpan span)
{
    switch (span)
    {
        case GraphDataTimeSpan.Yearly:
            return source.GroupBy(e => new DateTime(dateSelector(e).Year,1,1));
        case GraphDataTimeSpan.Monthly:
            return source.GroupBy(e => new DateTime(dateSelector(e).Year,dateSelector(e).Month,1));
        case GraphDataTimeSpan.Weekly:
            // TO DO
            break;
        case GraphDataTimeSpan.Daily:
            // TO DO
            break;
    }

    return source.GroupBy(e => dateSelector(e));
}

Try it online

使用此数据集:

List<Item> dates = new List<Item>{
    new Item(new DateTime(1901,1,1)),
    new Item(new DateTime(1902,1,1)),
    new Item(new DateTime(1902,2,1)),
    new Item(new DateTime(1903,4,1)),
    new Item(new DateTime(1902,1,2)),
    new Item(new DateTime(1905,1,3)),
    new Item(new DateTime(1907,2,1))
};

以下

foreach(var d in dates.AsQueryable().DateGroup(e => e.Date, GraphDataTimeSpan.Yearly))
{
    Console.WriteLine(d.Key.ToString("yyyy"));
    foreach(var d2 in d)
        Console.WriteLine("  |"+d2.Date.ToString("dd/MM/yyyy"));
}

给我:

1901
  |01/01/1901
1902
  |01/01/1902
  |01/02/1902
  |02/01/1902
1903
  |01/04/1903
1905
  |03/01/1905
1907
  |01/02/1907

并且:

foreach(var d in dates.AsQueryable().DateGroup(e => e.Date, GraphDataTimeSpan.Monthly))
{
    Console.WriteLine(d.Key.ToString("MM/yyyy"));
    foreach(var d2 in d)
        Console.WriteLine("  |"+d2.Date.ToString("dd/MM/yyyy"));
}

给我:

01/1901
  |01/01/1901
01/1902
  |01/01/1902
  |02/01/1902
02/1902
  |01/02/1902
04/1903
  |01/04/1903
01/1905
  |03/01/1905
02/1907
  |01/02/1907

答案 1 :(得分:0)

我摆弄着类似的东西。 也许看看Linq generic GroupBy date and other fields at the same time

我使用了一个单独的类来存储分组密钥。

public class DateGroupKey
{
    public int Year { get; set; }
    public int Month { get; set; }
    public int Week { get; set; }
    public int Day { get; set; }
}

扩展方法如下:

public static IEnumerable<IGrouping<DateGroupKey, TValue>> GroupByDate<TValue>(
    this IEnumerable<TValue> source,
    Func<TValue, DateTime> dateSelector,
    DateGroupType dateGroupType)
{
    Func<TValue, DateGroupKey> keySelector = null;
    switch (dateGroupType)
    {
        case DateGroupType.Year:
            keySelector = val => new DateGroupKey { Year = dateSelector(val).Year };
            break;
        case DateGroupType.Month:
            keySelector = val => new DateGroupKey { Year = dateSelector(val).Year, Month = dateSelector(val).Month };
            break;
        case DateGroupType.Week:
            keySelector = val => new DateGroupKey { Year = dateSelector(val).Year, Week = dateSelector(val).GetIso8601WeekOfYear() };
            break;
        case DateGroupType.Day:
            keySelector = val => new DateGroupKey { Year = dateSelector(val).Year, Month = dateSelector(val).Month, Day = dateSelector(val).Day };
            break;
        default:
            throw new NotSupportedException($"Group type not supported: {dateGroupType}");
    }

    return source.GroupBy(keySelector, new DateGroupKeyComparer());
}

和相等比较器:

public class DateGroupKeyComparer : IEqualityComparer<DateGroupKey>
{
    public bool Equals(DateGroupKey x, DateGroupKey y)
    {
        return x.Year == y.Year &&
            x.Month == y.Month &&
            x.Week == y.Week &&
            x.Day == y.Day;
    }

    public int GetHashCode(DateGroupKey obj)
    {
        unchecked
        {
            var h = 0;
            h = (h * 31) ^ obj.Year;
            h = (h * 31) ^ obj.Month;
            h = (h * 31) ^ obj.Week;
            h = (h * 31) ^ obj.Day;
            return h;
        }
    }
}

使用方式

var grouped = results.GroupByDate<ProductDto>(x => x.TimeStamp, DateGroupType.Month);