如何在实体框架中查找具有指定日期范围列表的日期?

Posted

技术标签:

【中文标题】如何在实体框架中查找具有指定日期范围列表的日期?【英文标题】:How to find a date with a list of specified date ranges in Entity Framework? 【发布时间】:2016-08-10 18:38:59 【问题描述】:

我创建了一个包含日期范围的类的 IEnumerable。该类如下所示:

public class Range<T>
    where T: struct 

    public T Start  get; set; 
    public T End  get; set; 

我想在我的集合中查找日期列在任何指定日期范围内的所有记录。这是我的尝试:

deals = deals.Where(
            deal =>
                criteria.DateRanges.Any(
                  dt =>
                    deal.CloseDate >= dt.Start &&
                    deal.CloseDate < dt.End.Value.AddDays(1)));

我认为这会引发一个错误,因为 EF 不知道如何将 criteria.DateRanges.Any() 转换为 SQL。那么,您将如何编写此代码来查找与任何日期范围匹配的日期?

【问题讨论】:

如果日期范围保存在内存中而不是数据库中,那么如果您想在数据库上运行此查询,那么您将需要找到一种方法将它们发送过来。 EF 不知道如何将您的日期类转换为参数。 @stuartd,因此提出了这个问题 好吧,如果我不得不问他们是否在内存中,那么你没有很好地解释它。有多少范围,达到一个数量级? 日期范围由用户指定。我怀疑永远不会超过 5 个。 使用 IQueryable 和 a predicate builder 动态构建查询是一种方法 【参考方案1】:

您可以为此使用LinqKit:

var expr = PredicateBuilder.False<Deal>();
foreach(var range in criteria.DateRanges)
   expr = expr.Or(d => dt.CloseDate >= range.Start && dt.CloseDate < range.End);
deals = deals.AsExpandable().Where(expr);

另一种选择是使用Expression Trees,但这对于您正在尝试做的事情来说似乎有点矫枉过正。

【讨论】:

这在没有AsExpandable() 的情况下不起作用。最好使用Universal PredicateBuilder。 @IvanStoev 谢谢,我在输入答案时忘记了AsExpandable,编辑于【参考方案2】:

同意@stuartd,如果有几个范围条件,您可以构建自己的表达式,为每个范围创建一个And 表达式,并在最后将它们组合成几个Ors。你可以像这样创建一个静态泛型方法:

public static Expression<Func<T,bool>> RangeExpression<T>(string property, IEnumerable<Range<DateTime>> criterias )

            Expression result = null;
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "o");
            foreach (var item in criterias)
            
                var value1 = item.Start.Date;
                var value2 = item.End.Date.AddDays(1);

                MemberExpression memberExpression1 = Expression.PropertyOrField(parameterExpression, property);
                MemberExpression memberExpression2 = Expression.PropertyOrField(parameterExpression, property);

                ConstantExpression valueExpression1 = Expression.Constant(value1, typeof(DateTime));
                ConstantExpression valueExpression2 = Expression.Constant(value2, typeof(DateTime));

                BinaryExpression binaryExpression1 = Expression.GreaterThanOrEqual(memberExpression1, valueExpression1);
                BinaryExpression binaryExpression2 = Expression.LessThan(memberExpression2, valueExpression2);

                var ret1 = Expression.Lambda<Func<T, bool>>(binaryExpression1, parameterExpression);
                var ret2 = Expression.Lambda<Func<T, bool>>(binaryExpression2, parameterExpression);

                Expression and = Expression.And(ret1, ret2);

                result = result!=null?Expression.OrElse(result, and):and;   
            
            return Expression.Lambda < Func<T,bool>>(result, parameterExpression);

稍后在您的代码中,您可以执行以下操作:

var rangeExp=RangeExpression<Deal>("CloseDate",criteria.DateRanges);
deals = deals.Where(rangeExp);

【讨论】:

以上是关于如何在实体框架中查找具有指定日期范围列表的日期?的主要内容,如果未能解决你的问题,请参考以下文章

在最接近指定日期的列表中查找上一个日期

如何使用函数匹配指定日期在另一个表日期范围内对应的数据?

如何生成列表而忽略日期不符合我要查找的范围的记录?

SQL Server:填写每个实体具有不同日期范围的缺失日期

具有日期范围的 CoreData 复合提取的意外结果

Mysql生成任意指定两时间范围内的日期列表