[英]grouping and flatening 1:M:M:1 related data in Entity Framework Core/LINQ
[英]Grouping data based on date entity framework and LINQ
我的数据库中有一系列分析事件,我想将此按日期分组的数据发送到我的客户端应用程序。
来自数据库的数据看起来像这样(但有数百条记录):
[
{
"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
}
然后我使用这些变量来计算和硬编码返回数据的名称。
正如你所看到的,有很多等等,因为代码变得荒谬了!
必须有一种更简单的方法来做到这一点,但我似乎找不到一个!
任何援助将不胜感激。
谢谢
首先要做的是按您感兴趣的 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
您需要添加一些复杂性。 首先是一种可以计算出 2 个日期之间的所有月/年组合的方法。
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}");
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.