表达式树和AND条件

Las*_*son 7 c# filter expression-trees conditional-statements

我正在尝试构建一个过滤器表达式来过滤数据库中的数据.

我编写了以下扩展来动态构建表达式,具体取决于所选的过滤器参数:

public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> leftExpression, 
        Expression<Func<T, bool>> rightExpression)
{
    var invocationExpression = Expression.Invoke(rightExpression, leftExpression.Parameters.Cast<Expression>());
    var andExpression = Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(leftExpression.Body, invocationExpression), 
        leftExpression.Parameters);

    return andExpression;
}
Run Code Online (Sandbox Code Playgroud)

我正在以这种方式使用它:

Expression<Func<MyObject, bool>> expression = x => true;

if(MyFilter.SomeParam) {
    expression = expression.And(x=>x.MyProperty == MyFilter.SomeParam);
}
Run Code Online (Sandbox Code Playgroud)

它适用于NHibernate,但是当我在Entity Framework 5中使用此代码时,它会失败,并显示以下异常消息:

LINQ to Entities不支持LINQ表达式节点类型"Invoke".

有一种解决方法从数据库中提取整个集合,然后通过IEnumerable.Where(Func<T1, T2> filterClause)它应用过滤条件,但它不需要所有数据只是为了获得一条记录,而Expression<Func<T1, T2>>表达式直接转换为SQL语句.

是否有任何简单的方法可以使此代码与Entity Framework一起使用?

pol*_*ios 6

面向域的N层.NET 4.0示例应用程序中尝试此实现(还有规范模式的实现):

public static class ExpressionBuilder
{
    public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    {
        var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.And);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.Or);
    }

}

public class ParameterRebinder : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> map;

    public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    {
        this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    }

    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    {
        return new ParameterRebinder(map).Visit(exp);
    }

    protected override Expression VisitParameter(ParameterExpression p)
    {
        ParameterExpression replacement;
        if (map.TryGetValue(p, out replacement))
        {
            p = replacement;
        }

        return base.VisitParameter(p);
    }

}
Run Code Online (Sandbox Code Playgroud)