C# .DefaultIfEmpty()基于日期值
我有一个实体框架模型C# .DefaultIfEmpty()基于日期值,c#,json,linq,C#,Json,Linq,我有一个实体框架模型Schedule,它映射到一个表dbo.Schedule。Schedule中的两个字段是hours(decimal)和week\u ending(DateTime) 我想将时间表中的数据输出为JSON,如下所示: { "week": [ "2017-08-11", "2017-08-18", "2017-08-25", "2017-09-01"], "hours": [ 40
Schedule
,它映射到一个表dbo.Schedule
。Schedule
中的两个字段是hours
(decimal
)和week\u ending
(DateTime
)
我想将时间表中的数据输出为JSON,如下所示:
{
"week": [
"2017-08-11",
"2017-08-18",
"2017-08-25",
"2017-09-01"],
"hours": [
40,
40,
0,
30]
}
换句话说,我想将结束的week\u
和hours
结果连接到两个数组中,其中结果按week分组,并在没有该周的记录时为hours
插入一个0
值
我知道.DefaultIfEmpty()
可以实现后一种功能,但我不知道如何将“empty”定义为“两个查询日期之间的间隔超过7天”(即缺少一周)<代码>周末
值始终为星期五,因此它们总是相隔7天
我不知道从哪里开始。。。我有一个相当基本的LINQ查询,如下所示(省略了不相关的Where
子句):
它在序列化后生成此JSON:
[
{
"week": "2017-08-11",
"hours": 20
},
// I would like for this record to be grouped
// with the first into one "hours": 40 record
{
"week": "2017-08-11",
"hours": 20
},
{
"week": "2017-08-18",
"hours": 40
},
// there is no "2017-08-25 record in the DB,
// but I would like for one to be printed with "hours": 0
{
"week": "2017-09-01",
"hours": 30
}
]
你可以考虑下面的方法
枚举开始/结束日期之间符合条件的计划的集合
确定并添加缺失的日期,然后排序结果
创建一个数据
对象以匹配所需的输出
代码可能如下所示,以所需格式保留数据
:
// Simulate linq connection
var db = new
{
Schedules = new List<Schedule>
{
new Schedule() { hours = 40, week_ending = new DateTime(2017,8,11) },
new Schedule() { hours = 20, week_ending = new DateTime(2017,8,18) }, // Simulating multiple records
new Schedule() { hours = 20, week_ending = new DateTime(2017,8,18) }, // Simulating multiple records
// No records for 8/25
new Schedule() { hours = 30, week_ending = new DateTime(2017,9,1) },
}
};
// Need a start/end date so you can generate missing weeks
var startDate = new DateTime(2017, 8, 11);
var endDate = new DateTime(2017, 9, 1);
// Enumerate schedules from db
var schedules = db.Schedules // Add any other criteria besides date logic
.Where(m => m.week_ending >= startDate && m.week_ending <= endDate)
.GroupBy(m => m.week_ending)
.Select(m => new Schedule() { week_ending = m.Key, hours = m.Sum(s => s.hours) })
.AsEnumerable();
// Add missing dates
var results = Enumerable.Range(0, 1 + endDate.Subtract(startDate).Days)
.Select(m => startDate.AddDays(m))
.Where(m => m.DayOfWeek == DayOfWeek.Friday) // Only end of week
.Where(m => schedules.Any(s => s.week_ending == m) == false) // Add missing weeks
.Select(m => new Schedule() { week_ending = m, hours = 0 })
.Union(schedules)
.OrderBy(m => m.week_ending);
// Enumerate the ordered schedules matching your criteria
var data = new
{
week = results.Select(m => m.week_ending),
hours = results.Select(m => m.hours)
};
//模拟linq连接
var db=新
{
明细表=新列表
{
新时间表(){hours=40,week_ending=new DateTime(2017,8,11)},
新计划(){hours=20,week_ending=new DateTime(2017,8,18)},//模拟多个记录
新计划(){hours=20,week_ending=new DateTime(2017,8,18)},//模拟多个记录
//8月25日没有记录
新时间表(){hours=30,week_ending=new DateTime(2017,9,1)},
}
};
//需要开始/结束日期,以便生成缺少的周数
var startDate=新日期时间(2017年8月11日);
var endDate=新日期时间(2017年9月1日);
//从数据库枚举计划
var schedules=db.schedules//添加日期逻辑之外的任何其他条件
其中(m=>m.week\u ending>=开始日期和m.week\u ending m.week\u ending)
.Select(m=>newschedule(){week_ending=m.Key,hours=m.Sum(s=>s.hours)})
.AsEnumerable();
//添加缺少的日期
var结果=可枚举范围(0,1+结束日期减去(开始日期).Days)
.Select(m=>startDate.AddDays(m))
.Where(m=>m.DayOfWeek==DayOfWeek.Friday)//仅限周末
.Where(m=>schedules.Any(s=>s.week_end==m)==false)//添加缺少的周数
.Select(m=>newschedule(){week_ending=m,hours=0})
.工会(附表)
.OrderBy(m=>m.week\u结束);
//枚举符合条件的已排序计划
var数据=新
{
周=结果。选择(m=>m.week\U ending),
小时数=结果。选择(m=>m.hours)
};
进近大纲:
- 每周的现有工时总和
- 生成一系列日期,然后
- 。。。对于范围内的每个日期,生成一个零小时的空计划
- 然后根据上面的总小时数对该列表进行修剪
- 最后,将结果组合起来,给出整个范围的完整列表
//示例类
公课时间表
{
公共日期时间周{get;set;}
公共整数小时数{get;set;}
}
//样本数据
var scheduleTable=新列表();
Add(new Schedule(){week=new DateTime(2017,8,11),hours=20});
Add(new Schedule(){week=new DateTime(2017,8,11),hours=20});
Add(new Schedule(){week=new DateTime(2017,8,18),hours=30});
//将同一周的所有小时相加。
var summedSchedule=scheduleTable.GroupBy(
x=>x.week,
x=>x.hours,
(key,g)=>newschedule(){week=key,hours=g.Sum()}
);
//生成日期范围。您需要定义截止点。在这里
//10周的日期是通过将7天连续添加到
//开始日期(从上面找到的第一个日期)
VarDates=Enumerable.Range(1,10)。选择(x=>scheduleTable.First().week.AddDays(7*x));
//生成空计划,在该范围内每周分配零小时。
var zeroSchedules=dates.Select(x=>newschedule(){week=x,hours=0});
//找出有小时的星期的日期。
var fullWeeks=summedSchedule.Where(x=>x.hours>0)。选择(x=>x.week);
//使用上面的列表仅拉出不带小时的明细表对象。
var emptyWeeks=zeroSchedules.Where(x=>!fullWeeks.Contains(x.week));
//然后,将上述零小时工作周列表与开始时间合并
//(汇总)具有小时数的周的列表。
var combined=新列表();
组合.AddRange(空周);
合并。添加范围(汇总计划);
combined=combined.OrderBy(x=>x.week.ToList();
要解决的问题:您无法查询数据库中不存在的内容,例如缺少周数
你需要一些时间范围,否则你的结果中会有很多周五。。。我将该范围定义为两个DateTime
sfromIncl
和toExcl
简单的部分是:查询已经每周汇总小时数的数据库,并将结果放入字典
// SELECT week = sch.week_ending, hours = SUM(sch.hours)
// FROM dbo.Schedules sch
// WHERE sch.week_ending >= fromIncl AND sch.week_ending < toExcl
// GROUP BY sch.week_ending
var hoursByWeek = db.Schedules
.Where(sch => sch.week_ending >= fromIncl && sch.week_ending < toExcl)
.GroupBy(sch => sch.week_ending, (k, vs) => new { week = k, hours = vs.Sum(sch1 => sch1.hours) })
.ToDictionary(sch => sch.week, sch => sch.hours);
以原始查询为基础,对周进行分组,然后在范围中查找第一周/最后一周并生成周范围,然后将范围左键加入原始数据:
var groupeddata = from d in data
group d by d.week into dg
select new { week = dg.Key, hours = dg.Sum(d => d.hours)};
var beginDate = groupeddata.Select(d => d.week).Min();
var endDate = groupeddata.Select(d => d.week).Max();
var weeks = Enumerable.Range(0, (endDate-beginDate).Days / 7 + 1).Select(n => beginDate.AddDays(7*n)).ToList();
var ans = from w in weeks
join s in groupeddata on w equals s.week into sj
from s in sj.DefaultIfEmpty()
select new { week = w, hours = (s == null ? 0 : s.hours) };
所以你想按时间的长短和日历的顺序来分组周,对吗?不,只是按日历的顺序。如果同一周有多个记录,我希望将小时值加在一起。例如,如果我有两个时间表
,都是以周结束=2017-08-11
,一个是小时=10
,另一个是小时=30
,那么输出将有一个si
public static IEnumerable<DateTime> EnumerateFridays(DateTime fromIncl, DateTime toExcl)
{
fromIncl = fromIncl.Date; // just to be sure
switch (fromIncl.DayOfWeek)
{
case DayOfWeek.Sunday: fromIncl = fromIncl.AddTicks(5 * TimeSpan.TicksPerDay); break;
case DayOfWeek.Monday: fromIncl = fromIncl.AddTicks(4 * TimeSpan.TicksPerDay); break;
case DayOfWeek.Tuesday: fromIncl = fromIncl.AddTicks(3 * TimeSpan.TicksPerDay); break;
case DayOfWeek.Wednesday: fromIncl = fromIncl.AddTicks(2 * TimeSpan.TicksPerDay); break;
case DayOfWeek.Thursday: fromIncl = fromIncl.AddTicks(TimeSpan.TicksPerDay); break;
// case DayOfWeek.Friday: break;
case DayOfWeek.Saturday: fromIncl = fromIncl.AddTicks(6 * TimeSpan.TicksPerDay); break;
}
Debug.Assert(fromIncl.DayOfWeek == DayOfWeek.Friday);
for (; fromIncl < toExcl; fromIncl = fromIncl.AddTicks(7 * TimeSpan.TicksPerDay))
yield return fromIncl;
}
public static TValue ValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default(TValue))
{
TValue value;
return dictionary.TryGetValue(key, out value) ? value : defaultValue;
}
MyAwesomeClass.EnumerateFridays(fromIncl, toExcl)
.Select(friday => new { week = friday, hours = hoursByWeek.ValueOrDefault(friday) })
var groupeddata = from d in data
group d by d.week into dg
select new { week = dg.Key, hours = dg.Sum(d => d.hours)};
var beginDate = groupeddata.Select(d => d.week).Min();
var endDate = groupeddata.Select(d => d.week).Max();
var weeks = Enumerable.Range(0, (endDate-beginDate).Days / 7 + 1).Select(n => beginDate.AddDays(7*n)).ToList();
var ans = from w in weeks
join s in groupeddata on w equals s.week into sj
from s in sj.DefaultIfEmpty()
select new { week = w, hours = (s == null ? 0 : s.hours) };