扩展访问表达式以检查值

时间:2014-08-22 14:38:58

标签: c# entity-framework expression-trees

我目前正试图与表达树搏斗以使一些魔法发生,但我在错误后一直遇到错误。

我的一些域对象(实体框架)

上有一些这样的属性
Expression<Func<DomainObject, LinkedDomainObject>> IncludeExpr
{
   get {
      return o => o.SomeLinkedObject;
   }
}

和另一个表达式,用于在某些属性(例如ID)上检查链接对象是否相等。

我确实有一个表达式也检查了该链接对象为null,这样我可以通过反转null检查表达式并通过{{1组合它来组成NotNullMatched ID表达式。使用ID检查表达式。

我想采用AndAlso表达式和o => o.SomeLinkedObject表达式并将它们混合在一起以有效地获取:

linkedObject => linkedObject.ID == idVar

但我不能为我的生活找出如何根据这两个单独的表达式得到一个表达式树。

1 个答案:

答案 0 :(得分:2)

我们可以花点时间创建一个帮助方法,可以非常简单地解决这个问题。如果我们创建一个方法,让我们像编写委托一样轻松地组合表达式,这变得非常容易。我们的Compose方法将接受一个表达式,另一个接受第一个表达式的输出并将其转换为其他表达式,创建一个新表达式,可以将第一个输入类型的某些内容转换为输出第二:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

这取决于以下方法将一个表达式的所有实例替换为另一个:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

现在我们可以非常轻松地创建IsNotNull转换:

public static Expression<Func<TSource, bool>> IsNotNull<TSource, TKey>(
    this Expression<Func<TSource, TKey>> expression)
{
    return expression.Compose(key => key != null);
}

至于And - 将两个表达式放在一起,如果使用LINQ查询提供程序,最简单的选择是分别在每个表达式上调用Where,如果这是一个选项。如果没有,您可以同时使用PrediacteBuilderAndOr两个表达式:

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(
            expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(
            expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
    }
}