4

我有一个 Event 类,其中包含开始和结束日期以及事件可能发生的特定日期:

public class Event
{
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public DayOfWeek[] DayOfWeekList { get; set; }
    public string Title { get; set; }
}

我可以让事件发生一次:

事件 1:StartDate = 9/15/2013,EndDate = 9/15/2013,DOW = 星期日

我可以举办多天的活动:

事件 2:StartDate = 9/15/2013,EndDate = 9/16/2013,DOW = 星期日、星期一

我可以让事件发生在某个日期范围内,但仅限于一周中特定的、可能不连续的日子,例如,在 9 月份,但只有周二和周四:

事件 3:StartDate = 9/1/2013,EndDate = 9/30/2013,DOW = 星期二、星期四

给定 9/1 - 9/16 的日期范围,我想在网格中将事件显示为一个事件日记录,如下所示:

活动 3:2013 年 9 月 3 日星期二
活动 3:2013 年 9 月 5 日星期四
活动 3:2013 年 9 月 10 日星期二
活动 3:2013 年 9 月 12 日星期四
活动一:2013 年 9 月 15 日星期日
活动 2:2013 年 9 月 15 日星期日
活动 2:2013 年 9 月 16 日星期一

我只会在数据库中存储一条事件记录来表示事件日,而不是每天一条记录。因此,对于上面的日期范围,我将从数据库中检索 3 个事件记录,并且需要根据使用事件的星期几的时间表为该日期范围生成一系列事件记录。

我将使用 Linq to SQL 检索 3 个事件记录,但随后需要扩展每个事件的时间表以创建新的事件记录,每天一个。这似乎效率不高,尤其是在网站被广泛使用的情况下。

有什么方法可以使用 Linq 完成吗?

另一种方法是在数据库中发生事件的每一天都有不同的子记录,但是如果有人将时间表从每周一更改为每周二,这将是一个痛苦的维护。我们也可以有没有定义结束日期的事件。

4

2 回答 2

3

这是一个蛮力解决方案,但这样的事情应该有效:

var startDate = new DateTime(2013, 9, 1);
var endDate = new DateTime(2013, 9, 16);
var totalDays = (int)(endDate - startDate).TotalDays + 1;
var results = 
    from i in Enumerable.Range(0, totalDays)
    let d = startDate.AddDays(i)
    from e in eventList 
    where e.StartDate <= d && 
          e.EndDate >= d &&
          e.DayOfWeekList.Contains(d.DayOfWeek)
    select new { Event = e, Date = d };

当我在内存中测试它时,这会产生正确的结果。不幸的是,这可能无法很好地转换为纯 Linq-to-SQL 代码。我认为您最好的选择是首先具体化事件子集,然后使用修改后的查询在内存中生成最终结果集:

var eventList = 
    (from e in db.Events 
     where e.StartDate <= endDate && 
           e.EndDate >= startDate
     select e)
    .AsEnumerable();
var results = 
    from i in Enumerable.Range(0, totalDays)
    let d = startDate.AddDays(i)
    from e in eventList 
    where e.StartDate <= d && 
          e.EndDate >= d &&
          e.DayOfWeekList.Contains(d.DayOfWeek) 
    select new { Event = e, Date = d };
于 2013-09-08T23:21:06.897 回答
1

如果您最常阅读事件,并且不介意节省一点额外成本,则可以选择根据规则将事件具体化到新表中。如果您愿意,可以根据规则清除并重新生成该表,或者根据需要选择性地重新填充该表。

保存后的事件 1 将创建一个条目,EventID:1

保存后的事件 2 将为与 DOW 属性匹配的日期范围创建条目,EventID:2

事件 3 会做同样的事情,但它的日期范围/DOW,EventID:3

如果需要,您可以使用一些花哨的 LINQ 来计算要添加的行,但最终结果是您需要物理实例行以便真正轻松查询。

更新事件时,您可以简单地删除该 EventID 的所有行并根据更改重新创建它们。

鉴于上述情况,您的读取操作可能很简单:

EventInstances.Where(i => i.Date >= startDate && i.Date <= endDate);

相反,如果您想要缓慢/复杂的读取和快速写入,则每次读取时都需要生成此内存映射。

更新:一些代码来说明我的意思

填充表的逻辑与您可以用来创建内存表的逻辑完全相同:

// slightly updated Event class
public class Event
{
    public int ID { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public DayOfWeek[] DayOfWeekList { get; set; }
    public string Title { get; set; }
}

var startDate = new DateTime(2013, 9, 1);
var endDate = new DateTime(2013, 9, 16);
var totalDays = (int)endDate.Subtract(startDate).TotalDays + 1;

// sample data, including a 4th event starting a while ago with no end date
var events = new List<Event> {
    new Event { ID = 1, Title = "Event 1", StartDate = new DateTime(2013, 9, 15), EndDate = new DateTime(2013, 9, 15), DayOfWeekList = new[] { DayOfWeek.Sunday } },
    new Event { ID = 2, Title = "Event 2", StartDate = new DateTime(2013, 9, 15), EndDate = new DateTime(2013, 9, 16), DayOfWeekList = new[] { DayOfWeek.Sunday, DayOfWeek.Monday } },
    new Event { ID = 3, Title = "Event 3", StartDate = new DateTime(2013, 9, 1), EndDate = new DateTime(2013, 9, 30), DayOfWeekList = new[] { DayOfWeek.Tuesday, DayOfWeek.Thursday } },
    new Event { ID = 4, Title = "Event 4", StartDate = new DateTime(2013, 1, 1), EndDate = null, DayOfWeekList = new[] { DayOfWeek.Wednesday } },
};

var eventsInRange = events
    .Where(e => e.StartDate <= endDate)
    .Where(e => (e.EndDate == null || e.EndDate.Value >= startDate ))
    // if you are getting from the database, force this data to be 
    // retrieved since the following section would not work with the DB
    .AsEnumerable();

var daysInRange = Enumerable
    .Range(0, totalDays)
    .Select(i => startDate.AddDays(i));

var eventInstances = daysInRange
    .SelectMany(d => eventsInRange
        .Where(e => e.EndDate == null || d <= e.EndDate.Value)
        .Where(e => d >= e.StartDate)
        .Where(e => e.DayOfWeekList.Contains(d.DayOfWeek))
        .Select(e => new { Date = d, Day = d.DayOfWeek, Event = e }));

如果您想用这些数据填充表格以便于查询,只需根据合理的设置开始日期和结束日期(例如,1 年前开始,2 年结束时间)。

如果您想在更新后仅为一个事件重新填充,只需删除该事件 ID 的所有记录并将其限制eventsInRange为仅更新事件。

于 2013-09-08T23:45:08.147 回答