我有一个半复杂的函数,我在LINQ表达式中使用。该函数名为DatePeriodOverlap()
,如下所示:
bool DatePeriodOverlap(DateTime startDate, DateTime endDate, DateTime filterStartDate, DateTime filterEndDate)
{
return ((startDate >= filterStartDate && startDate <= filterEndDate)
||
(endDate <= filterEndDate && endDate >= filterStartDate)
||
(startDate <= filterStartDate && endDate >= filterEndDate));
}
现在,我想在LINQ to SQL场景(nHibernate)中使用它。这意味着,我不能只使用函数本身,但我必须创建一个表达式并使用它。所以我创建了这个函数:
Expression<Func<Domain.Course, bool>> GetDatePeriodOverlapForCourseExpression(DateTime filterStartDate, DateTime filterEndDate)
{
return x => (x.StartDate >= filterStartDate && x.StartDate <= filterEndDate)
||
(x.EndDate <= filterEndDate && x.EndDate >= filterStartDate)
||
(x.StartDate <= filterStartDate && x.EndDate >= filterEndDate);
}
我现在可以在我的查询中使用它而不会出现问题。但是,我必须为具有日期范围的每个SQL对象重写完全相同的函数。做这样的事情会不会更容易:
Expression<Func<TObject, bool>> GetDatePeriodOverlapExpression<TObject>(Expression<Func<TObject, DateTime>> StartDate, Expression<Func<TObject, DateTime>> EndDate, DateTime filterStartDate, DateTime filterEndDate)
{
return x => (StartDate(x) >= filterStartDate && StartDate(x) <= filterEndDate)
||
(EndDate(x)<= filterEndDate && EndDate(x) >= filterStartDate)
||
(StartDate(x) <= filterStartDate && EndDate(x) >= filterEndDate);
}
然后我可以这样称呼它:
var query = GetDatePeriodOverlapExpression<Domain.Course>((x => x.StartDate), (y => y.EndDate), filterStartDate, filterEndDate);
这似乎是一个很棒的主意。除了它不起作用。对于StartDate(x)
和EndDate(x)
的每次通话,都需要“方法,代理或事件”。就像我所知,我必须从头开始创建表达式树,不能使用LINQ来创建它。没错,我只需要做一次但看起来做LINQ应该做的很多工作似乎很多。
我尝试使用对StartDate.Compile()(x)
的调用来编译。但是当我运行它时,似乎比较函数指针而不是函数的结果。
有没有办法将Expression插入LINQ语句?
答案 0 :(得分:1)
您可以动态构建表达式树:
Expression<Func<TObject, bool>> GetDatePeriodOverlapExpression<TObject>(Expression<Func<TObject, DateTime>> StartDate, Expression<Func<TObject, DateTime>> EndDate, DateTime filterStartDate, DateTime filterEndDate)
{
var parameter = Expression.Parameter(typeof(TObject));
var e1 = Expression.GreaterThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, StartDate.Body), Expression.Constant(filterStartDate)); // StartDate(x) >= filterStartDate
var e2 = Expression.LessThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, StartDate.Body), Expression.Constant(filterEndDate)); // StartDate(x) <= filterEndDate
var e3 = Expression.AndAlso(e1, e2); // (StartDate(x) >= filterStartDate && StartDate(x) <= filterEndDate)
var e4 = Expression.LessThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, EndDate.Body), Expression.Constant(filterEndDate)); // EndDate(x) <= filterEndDate
var e5 = Expression.GreaterThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, EndDate.Body), Expression.Constant(filterStartDate)); // EndDate(x) >= filterStartDate
var e6 = Expression.AndAlso(e4, e5); // (EndDate(x) <= filterEndDate && EndDate(x) >= filterStartDate)
var e7 = Expression.LessThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, StartDate.Body), Expression.Constant(filterStartDate)); // StartDate(x) <= filterStartDate
var e8 = Expression.GreaterThanOrEqual(ParameterRebinder.ReplaceParameters(parameter, EndDate.Body), Expression.Constant(filterEndDate)); // EndDate(x) >= filterEndDate
var e9 = Expression.AndAlso(e7, e8); // (StartDate(x) <= filterStartDate && EndDate(x) >= filterEndDate)
var e10 = Expression.OrElse(Expression.OrElse(e3, e6), e9);
return Expression.Lambda<Func<TObject, bool>>(e10, parameter);
}
public class ParameterRebinder : ExpressionVisitor
{
private readonly ParameterExpression parameter;
public ParameterRebinder(ParameterExpression parameter)
{
this.parameter = parameter;
}
public static Expression ReplaceParameters(ParameterExpression parameter, Expression exp)
{
return new ParameterRebinder(parameter).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
return base.VisitParameter(parameter);
}
}
编辑:当我运行您的初始代码时,我收到错误&#34;已经添加了具有相同密钥的项目。&#34;我已使用修复后的代码更新了代码,并修复了逻辑中的复制粘贴问题。 - @ErikAllen
答案 1 :(得分:1)
您可以创建一个界面:
public interface IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
然后在要使用表达式的所有类中实现接口。你的表达式函数将变为:
Expression<Func<IDateSearchable, bool>> GetDatePeriodOverlapForCourseExpression(DateTime filterStartDate, DateTime filterEndDate)
{
return x => (x.StartDate >= filterStartDate && x.StartDate <= filterEndDate)
||
(x.EndDate <= filterEndDate && x.EndDate >= filterStartDate)
||
(x.StartDate <= filterStartDate && x.EndDate >= filterEndDate);
}
因为Getperiodoverlap ...方法返回一个接受IDateSearchable作为参数的函数,它将适用于任何实现该接口的类。
那么你的课就是
public class SomeClassName : IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
// all your other stuff
}