如何在EF Core中检查指定日期范围是否被数据表日期范围完全覆盖?
解决方案
首先明确两种需求场景,对应不同的实现方式:
场景1:找单个SupportItem完全包含检查范围
如果你的需求确实是给定日期范围完全落在某一条SupportItem的日期区间内(即检查的开始日期≥SupportItem.StartDate,检查的结束日期≤SupportItem.EndDate),那你原来的代码逻辑是正确的,但你的示例2预期结果应该是false(因为没有任何一条SupportItem的区间能包含09/02/2023 - 23/02/2023)。如果要优化代码的可读性,可以简化嵌套的Where和Any:
var checkStart = checkRequest.ActivityStartDate.Date; var checkEnd = checkRequest.ActivityEndDate.Date; var isSingleRangeCovered = activePlans .SelectMany(z => z.ServicePlanBudgetTypes) .SelectMany(y => y.SupportItems) .Any(x => x.StartDate.Date <= checkStart && x.EndDate.HasValue && x.EndDate.Value.Date >= checkEnd);
场景2:检查范围被多个SupportItem的区间联合覆盖
从你的示例来看,实际需求应该是给定日期范围的每一天都被至少一个SupportItem的区间覆盖(比如09/02/2023属于第一条,10/02-23/02属于第二条,联合覆盖整个检查范围)。这种情况需要先获取所有与检查范围有交集的SupportItem区间,合并连续/重叠的区间后,再检查是否能完全覆盖目标范围:
步骤1:获取相关的SupportItem区间
var checkStart = checkRequest.ActivityStartDate.Date; var checkEnd = checkRequest.ActivityEndDate.Date; // 获取所有与检查范围有交集的SupportItem,处理EndDate为null的情况(视为永久有效) var relevantRanges = activePlans .SelectMany(z => z.ServicePlanBudgetTypes) .SelectMany(y => y.SupportItems) .Where(x => x.StartDate.Date <= checkEnd && (x.EndDate == null || x.EndDate.Value.Date >= checkStart)) .Select(x => new { Start = x.StartDate.Date, End = x.EndDate?.Date ?? DateTime.MaxValue.Date }) .OrderBy(r => r.Start) .ToList();
步骤2:合并连续/重叠的区间
if (!relevantRanges.Any()) { // 没有任何相关区间,直接返回false return false; } var mergedRanges = new List<(DateTime Start, DateTime End)>(); mergedRanges.Add((relevantRanges[0].Start, relevantRanges[0].End)); foreach (var range in relevantRanges.Skip(1)) { var lastMerged = mergedRanges.Last(); // 如果当前区间的开始日期与上一个合并区间连续或重叠,则合并 if (range.Start <= lastMerged.End.AddDays(1)) { var newEnd = lastMerged.End > range.End ? lastMerged.End : range.End; mergedRanges.RemoveAt(mergedRanges.Count - 1); mergedRanges.Add((lastMerged.Start, newEnd)); } else { mergedRanges.Add((range.Start, range.End)); } }
步骤3:检查合并后的区间是否完全覆盖目标范围
bool isFullyCovered = true; DateTime currentDate = checkStart; foreach (var range in mergedRanges) { if (currentDate > checkEnd) break; // 存在未覆盖的缺口 if (range.Start > currentDate) { isFullyCovered = false; break; } // 更新当前覆盖到的日期(range.End是包含的,所以下一个要检查的是range.End+1) currentDate = range.End.AddDays(1); } // 检查是否覆盖到了检查范围的结束日期之后 if (currentDate <= checkEnd) { isFullyCovered = false; } // isFullyCovered就是最终结果
这个实现会处理你的示例场景:
- 示例1(09/02-24/02):24/02不在任何区间内,
isFullyCovered返回false - 示例2(09/02-23/02):合并后的区间是08/02-23/02,完全覆盖目标范围,返回
true
内容的提问来源于stack exchange,提问作者hanushi-thana
相关产品推荐
相关产品推荐

