将表达式插入到 LINQ-To-Sql 的 LINQ 语句中
Insert expression into LINQ statement for Linq-To-Sql
我有一个在 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);
这似乎是个很棒的主意。除了它不起作用。 "Method, delegate or event is expected" 每次调用 StartDate(x)
和 EndDate(x)
。据我所知,我必须从头开始创建表达式树,不能使用 LINQ 来创建它。的确,我只需要做一次,但要完成 LINQ 自然应该做的事情似乎需要做很多工作。
我试过使用对 StartDate.Compile()(x)
的调用来编译。但是当我 运行 它时,它似乎比较函数指针而不是函数的结果。
有没有办法将表达式插入到 LINQ 语句中?
您可以像这样动态构建表达式树:
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);
}
}
编辑:当我运行你的初始代码时,我得到了一个错误"An item with the same key has already been added."我已经用那个修复更新了代码,并修复了一个副本-在逻辑中粘贴问题。 - @ErikAllen
您可以创建一个界面:
public interface IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
然后在所有要使用表达式的 classes 中实现接口。您的表达式函数将变为:
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... 方法 returns 一个接受 IDateSearchable 作为参数的函数,它将在任何实现该接口的 class 上工作。
那么你的class就是
public class SomeClassName : IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
// all your other stuff
}
我有一个在 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);
这似乎是个很棒的主意。除了它不起作用。 "Method, delegate or event is expected" 每次调用 StartDate(x)
和 EndDate(x)
。据我所知,我必须从头开始创建表达式树,不能使用 LINQ 来创建它。的确,我只需要做一次,但要完成 LINQ 自然应该做的事情似乎需要做很多工作。
我试过使用对 StartDate.Compile()(x)
的调用来编译。但是当我 运行 它时,它似乎比较函数指针而不是函数的结果。
有没有办法将表达式插入到 LINQ 语句中?
您可以像这样动态构建表达式树:
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);
}
}
编辑:当我运行你的初始代码时,我得到了一个错误"An item with the same key has already been added."我已经用那个修复更新了代码,并修复了一个副本-在逻辑中粘贴问题。 - @ErikAllen
您可以创建一个界面:
public interface IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
然后在所有要使用表达式的 classes 中实现接口。您的表达式函数将变为:
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... 方法 returns 一个接受 IDateSearchable 作为参数的函数,它将在任何实现该接口的 class 上工作。
那么你的class就是
public class SomeClassName : IDateSearchable
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
// all your other stuff
}