繁体   English   中英

实体框架日期组合查询

[英]Entity Framework date combination query

我有一个Apartments列表,每个Apartment都有它的ApartmentRooms ,每个ApartmentRoom都有DateBatches 每个DateBatch都有Dates列表,表示占用日期(每个拍摄日期一条记录)。

Apartment 1
 Room 1
    DateBatch
      1.5.2015
      2.5.2015
    DateBatch 2
      8.5.2015
      9.5.2015
 Room 2
    DateBatch
      5.5.2015
      6.5.2015

通过这个,您可以看到这间公寓有2个房间,其中1号房间在下列日期分别为1.5,2.5,8.5和9.5,而房间2则分别为5.5和6.5。

用户可以输入他想要留下的N天和X连续天数的期望时段。

举例来说,用户输入的时间从1.5到15.5并且他想要睡10个晚上,我需要列出所有公寓,其中至少有一个公寓房可用于任何可能的日期组合,在这种情况下将遵循:

1.5-10.5
2.5-11.5
3.5-12.5
4.5-13.5
5.5-14.5

到目前为止,我已经尝试过这个,它只适用于第一次foreach迭代,因为foreach将查询与AND标准连接,而不是OR标准,我认为这是一个非常糟糕的方法。

 public static IQueryable<Apartment> QueryByPeriod(this IQueryable<Apartment> apartments, DateTime PeriodStart, DateTime PeriodEnd, int StayDuration)
 {
        var possibleDateRanges = new List<List<DateTime>>();

        //set all possible start dates for the desired period
        for (var i = PeriodStart; i <= PeriodEnd.AddDays(-StayDuration); i = i.AddDays(1))
        {
            List<DateTime> dates = new List<DateTime>();

            foreach(var date in i.DateRange(i.AddDays(StayDuration-1)))
            {
                dates.Add(date);
            }

            possibleDateRanges.Add(dates);
        }

        //filter by date range
        //select apartment rooms where one of possible combinations is suitable for selected period
        foreach (var possibleDates in possibleDateRanges)
        {
            apartments = apartments.Where(m => m.ApartmentRooms
            .Any(g => g.OccupiedDatesBatches.Select(ob => ob.OccupiedDates).Any(od => od.Any(f => possibleDates.Contains(f.Date)))
            ) == false);
        }

        return apartments;
    }

有什么建议?

要将多个条件与OR组合,您可以使用(例如)LinqKit库。 通过nuget安装, using LinqKit;添加using LinqKit; 然后:

    public static IQueryable<Apartment> QueryByPeriod(this IQueryable<Apartment> apartments, DateTime PeriodStart, DateTime PeriodEnd, int StayDuration) {
        var possibleDateRanges = new List<Tuple<DateTime, DateTime>>();
        // list all ranges, so for your example that would be:
        //1.5-10.5
        //2.5-11.5
        //3.5-12.5
        //4.5-13.5
        //5.5-14.5            
        var startDate = PeriodStart;
        while (startDate.AddDays(StayDuration - 1) < PeriodEnd) {
            possibleDateRanges.Add(new Tuple<DateTime, DateTime>(startDate, startDate.AddDays(StayDuration - 1)));
            startDate = startDate.AddDays(1);
        }
        Expression<Func<Apartment, bool>> condition = null;
        foreach (var range in possibleDateRanges) {                
            Expression<Func<Apartment, bool>> rangeCondition = m => m.ApartmentRooms       
                // find rooms where ALL occupied dates are outside target interval             
                .Any(g => g.OccupiedDatesBatches.SelectMany(ob => ob.OccupiedDates).All(f => f.Date < range.Item1 || f.Date > range.Item2)
                );
            // concatenate with OR if necessary
            if (condition == null)
                condition = rangeCondition;
            else
                condition = condition.Or(rangeCondition);
        }
        if (condition == null)
            return apartments;
        // note AsExpandable here
        return apartments.AsExpandable().Where(condition);
    }

请注意,我也修改了你的逻辑。 当然,这个逻辑是单元测试的理想选择,如果你正在开展一个严肃的项目 - 你应该使用内存中的EF提供程序(或模拟)来针对不同的条件进行定义测试。

这是一个纯粹的(不需要外部包)LINQ to Entities解决方案。

首先确定可能的开始日期列表:

var startDates = Enumerable.Range(0, PeriodEnd.Subtract(PeriodStart).Days - StayDuration + 1)
    .Select(offset => PeriodStart.AddDays(offset))
    .ToList();

然后使用以下查询:

var availableApartments = apartments.Where(a => a.ApartmentRooms.Any(ar =>
    startDates.Any(startDate => !ar.OccupiedDatesBatches.Any(odb => 
        odb.OccupiedDates.Any(od => 
            od.Date >= startDate && od.Date < DbFunctions.AddDays(startDate, StayDuration))))));

该解决方案的好处是可以轻松扩展。 上述查询返回可用的公寓,但不提供可用房间的信息以及何时 - 您可能需要向用户提供的信息。 使用上述方法,您可以获得如下信息:

public class AvailableApartmentInfo
{
    public Apartment Apartment { get; set; }
    public Room Room { get; set; }
    public DateTime StartDate { get; set; }
}

var availableApartmentInfo =
    from a in apartments
    from ar in a.ApartmentRooms
    from startDate in startDates
    where !ar.OccupiedDatesBatches.Any(odb => 
        odb.OccupiedDates.Any(od => 
            od.Date >= startDate && od.Date < DbFunctions.AddDays(startDate, StayDuration)))
    select new AvailableApartmentInfo { Apartment = a, Room = ar, StartDate = startDate };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM