【问题标题】:Grouping data based on date entity framework and LINQ基于日期实体框架和 LINQ 对数据进行分组
【发布时间】:2019-08-29 09:46:47
【问题描述】:

我的数据库中有一组分析事件,我想将这些按日期分组的数据发送到我的客户端应用程序。

数据库中的数据看起来像这样(但有数百条记录):

[
  {
    "DateAdded": "2006-12-30 00:38:54",
    "Event": "click",
    "Category": "externalWebsite"
  },
  {
    "DateAdded": "2006-07-20 00:36:44",
    "Event": "click",
    "Category": "social"
  },
  {
    "DateAdded": "2006-09-20 00:36:44",
    "Event": "click",
    "Category": "social"
  },
  {
    "DateAdded": "2006-09-22 00:12:34",
    "Event": "load",
    "Category": "profile"
  }
]

我想要做的是返回所有说“社交”“点击”的计数,但按月返回,所以它看起来像这样:

[
  {
    "name": "socialclicks",
    "series": [
      {
        "count": 259,
        "name": "Jan"
      },
      {
        "count": 0,
        "name": "Feb"
      },
      {
        "count": 52,
        "name": "Mar"
      }
      ... etc, etc up to Dec <====
    ]
  }
]

所以,我一直在尝试使用他们的 id 获取与特定用户关联的所有记录。这很简单。

现在我需要将它们的记录拆分为每月计数,显示从当前月份开始的最近 12 个月(如果该月份不存在,则返回 0) - 这被证明是复杂和困难的。

我的做法是这样的:

var records = context.records.where(r => r.Id = userId).ToList();
var jan
var feb
var mar
var apr
... etc, etc

for (int i = 0; i < records.Count ; i++)
{
    if (record.DateAdded > "2005-12-31 00:00.00" && record.DateAdded < "2006-01-31 00:00.00") {
       jan++;
    }

    if (record.DateAdded > "2006-01-31 00:00.00" && record.DateAdded < "2006-02-28 00:00.00") {
       feb++;
    }

    ...etc, etc
}

然后我使用这些变量来计算和硬编码返回数据的名称。

正如你所看到的,有很多等等,因为代码变得荒谬!

一定有更简单的方法可以做到这一点,但我似乎找不到!

我们将不胜感激。

谢谢

【问题讨论】:

  • 您正在研究的条目是在同一年创建的吗?
  • 不幸的是没有,我希望只返回过去 12 个月,但我认为稍后会解决这个问题! IE。如果是 2018 年 8 月,我会从 2017 年 8 月到 2018 年 8 月返回,任何为空的都返回 0。
  • 你要么用这些额外的要求更新你的问题,否则我不会将它们视为问题范围的一部分。
  • 好的,谢谢,已按照建议更新了我的问题。

标签: c# .net entity-framework linq


【解决方案1】:

首先要做的是将所有数据按您感兴趣的 2 个属性分组

  • 事件
  • 类别

例子:

 var partialResult = entries.GroupBy(x => new {
       x.Event,
       x.Category
    });

从那里,当您预测结果时,您可以按月和年再次分组。 - 用于演示的匿名对象,但您可以轻松地将其定义为适当的结构/类:

var result = entries.GroupBy(x => new {
       x.Event,
       x.Category
    }).Select(g => new {
       g.Key.Event,
       g.Key.Category,
       Series = g.GroupBy(x => new {x.DateAdded.Month, x.DateAdded.Year})
                 .Select(i => new{
                         i.Key.Month,
                         i.Key.Year,
                         Count = i.Count()
                }).ToArray()
    });

foreach(var item in result)
{
    Console.WriteLine($"Event:{item.Event} Category:{item.Category}");
    foreach(var serie in item.Series)
        Console.WriteLine($"\t{CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(serie.Month)}{serie.Year} Count={serie.Count}");
}

编辑:为了满足您的要求:

如果月份不存在,则返回 0

您需要添加一些复杂性。第一种方法可以计算出两个日期之间的所有月/年组合。

private static IEnumerable<(int Month, int Year)> MonthsBetween(
        DateTime startDate,
        DateTime endDate)
{
    DateTime iterator;
    DateTime limit;

    if (endDate > startDate)
    {
        iterator = new DateTime(startDate.Year, startDate.Month, 1);
        limit = endDate;
    }
    else
    {
        iterator = new DateTime(endDate.Year, endDate.Month, 1);
        limit = startDate;
    }

    var dateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat;
    while (iterator < limit)
    {
        yield return (iterator.Month,iterator.Year);                
        iterator = iterator.AddMonths(1);
    }
}

您还需要某种范围来计算两者之间的所有月份,并过滤您的原始查询:

var dateRangeStart = DateTime.Parse("2006-01-01");
var dateRangeEnd = DateTime.Parse("2007-01-01");
var monthRange = MonthsBetween(dateRangeStart,dateRangeEnd);

var results = entries.Where(e => e.DateAdded>=dateRangeStart && e.DateAdded<dateRangeEnd)
     ..... etc

然后,在输出结果时,您需要有效地对您的年/月列表进行左连接。出于某种原因,使用查询语法比使用 lambda 更容易。

foreach(var item in results)
{
    Console.WriteLine($"Event:{item.Event} Category:{item.Category}");

    var joinedSeries = from month in monthRange
                        join serie in item.Series
                        on new{month.Year, month.Month} equals new {serie.Year, serie.Month} into joined
                        from data in joined.DefaultIfEmpty()
                        select new {
                            Month = data == null ? month.Month : data.Month,
                            Year = data == null ? month.Year : data.Year,
                            Count = data == null ? 0 : data.Count
                        };


foreach(var serie in joinedSeries)
    Console.WriteLine($"\t{CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(serie.Month)}{serie.Year} Count={serie.Count}");
}

现场示例:https://dotnetfiddle.net/K7ZoJN

【讨论】:

  • Magic :) 非常感谢,这样好多了!
  • @DBoi 没有问题。刚刚意识到这不会给您带来数据中不存在的几个月。有问题吗?
  • 理想情况下,我希望拥有过去 12 个月内的所有月份,并为任何不存在的月份返回 0,这样用户就知道它是否有道理?
  • 这是有道理的,你有两个选择。 1) 将其视为 UI 问题并在那里处理或 2) 将您的结果加入预先设定的月份。 2是繁琐的,但可行的。如果您需要,我可以更新答案
  • 是的,请,我认为在服务器端处理它会更好 - 谢谢你,非常感谢帮助
猜你喜欢
  • 2016-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
相关资源
最近更新 更多