将表达式插入Linq-To-Sql的LINQ语句中

时间:2015-12-21 16:24:13

标签: c# linq nhibernate 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);

这似乎是一个很棒的主意。除了它不起作用。对于StartDate(x)EndDate(x)的每次通话,都需要“方法,代理或事件”。就像我所知,我必须从头开始创建表达式树,不能使用LINQ来创建它。没错,我只需要做一次但看起来做LINQ应该做的很多工作似乎很多。

我尝试使用对StartDate.Compile()(x)的调用来编译。但是当我运行它时,似乎比较函数指针而不是函数的结果。

有没有办法将Expression插入LINQ语句?

2 个答案:

答案 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
}