.DefaultIfEmpty()基于日期值

时间:2017-08-03 16:54:55

标签: c# json linq

我有一个实体框架模型Schedule,它已映射到表dbo.ScheduleSchedule中的两个字段为hoursdecimal)和week_endingDateTime)。

我想将Schedule表中的数据输出到JSON,如下所示:

{
   "week": [
         "2017-08-11",
         "2017-08-18",
         "2017-08-25",
         "2017-09-01"],
   "hours": [
         40,  
         40,
          0,
         30]
}

换句话说,我想将week_endinghours结果连接成两个数组,其中结果为grouped by周,并为{插入0值{1}}当那个星期没有记录时。

我知道hours可以完成后者之类的事情,但我不知道如何将“空”定义为“两个查询日期之间有超过7天”(即一周是失踪)。 .DefaultIfEmpty()值始终是星期五,所以它们总是相隔7天。

我不知道从哪里开始...我有一个相当基本的LINQ查询,如此(省略了无关的week_ending子句):

Where

在序列化后产生这个JSON:

var data =
  db.Schedules
    .OrderBy(s => s.week_ending)
    .Select(s => new
    { 
        week = s.week_ending,
        hours = s.hours
    });

4 个答案:

答案 0 :(得分:2)

您可以考虑采用以下方法

  1. 枚举与开始/结束日期之间的条件匹配的计划的汇总
  2. 识别并添加缺失日期,然后订购结果
  3. 创建data对象以匹配所需的输出
  4. 代码可能如下所示,以data格式保留所需格式:

    // 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)
    };
    

答案 1 :(得分:0)

方法概要:

  • 每周的现有小时数。
  • 生成一系列日期,然后......
  • ...对于范围中的每个日期,生成零小时的空计划
  • 然后根据上面的总计小时修剪此列表。
  • 最后,将结果组合在一起,给出整个范围的完整列表。

// sample class
public class Schedule
{
    public DateTime week {get; set;}
    public int hours {get; set;}
}

// sample data
var scheduleTable = new List<Schedule>();

scheduleTable.Add(new Schedule() { week=new DateTime(2017,8,11), hours=20});
scheduleTable.Add(new Schedule() { week=new DateTime(2017,8,11), hours=20});
scheduleTable.Add(new Schedule() { week=new DateTime(2017,8,18), hours=30});

// Sum all hours that fall on the same week.
var summedSchedule = scheduleTable.GroupBy(
    x => x.week, 
    x => x.hours, 
    (key, g) => new Schedule() { week = key, hours = g.Sum() }
);

// Generate range of dates. You'll need to define the cut off point. Here,
// 10 weeks of dates are generated by adding 7 days successively to the
// starting date (the first date found from above)
var dates = Enumerable.Range(1, 10).Select(x => scheduleTable.First().week.AddDays(7 * x));

// Generate empty schedules, assigning zero hours to every week in the range.
var zeroSchedules = dates.Select(x => new Schedule() { week = x, hours = 0 });

// Pull out the dates for the weeks that have hours.
var fullWeeks = summedSchedule.Where(x => x.hours > 0).Select(x => x.week);

// Use the above list to pull out only the Schedule objects without hours.
var emptyWeeks = zeroSchedules.Where(x => !fullWeeks.Contains(x.week));

// The above list of zero-hour-weeks is then combined with the starting
// (summed) list of weeks that have hours.
var combined = new List<Schedule>();
combined.AddRange(emptyWeeks);
combined.AddRange(summedSchedule);
combined = combined.OrderBy(x => x.week).ToList();

答案 2 :(得分:0)

要解决的问题:您无法查询数据库中没有的内容,例如错过了几周。

您需要一些时间范围,因为否则您的结果中会有很多星期五...我会将该范围定义为两个DateTime s fromIncl和{{ 1}}。

简单的部分:查询数据库已经按周总计小时数并将结果放入字典中。

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);

给出另一个方便的扩展方法:

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;
}

答案 3 :(得分:0)

使用原始查询作为基础,对周数进行分组,然后找到范围中的第一周/最后一周并生成一系列周数,然后将范围加入到原始数据中:

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) };