【问题标题】:Extending an access expression to check for value扩展访问表达式以检查值
【发布时间】:2014-08-22 14:38:57
【问题描述】:

我目前正在尝试与表达式树搏斗,以使一些神奇的事情发生,但我不断地遇到一个又一个错误。

我的一些域对象(实体框架)有一些类似的属性

Expression<Func<DomainObject, LinkedDomainObject>> IncludeExpr
{
   get {
      return o => o.SomeLinkedObject;
   }
}

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

我确实有一个表达式也检查了该链接对象是否为空,这样我可以通过反转空检查表达式并通过 AndAlso 将其与 ID 检查组合来组成 NotNullMatched ID 表达式表达。

我想将o =&gt; o.SomeLinkedObject 表达式和linkedObject =&gt; linkedObject.ID == idVar 表达式混合在一起以有效地得到:

o =&gt; o.LinkedObject != null &amp;&amp; o.LinkedObject.Id == idVar

但我这辈子都想不出如何根据这两个单独的表达式组合出一个表达式树。

【问题讨论】:

    标签: c# entity-framework expression-trees


    【解决方案1】:

    我们可以花点时间创建一个帮助方法,它可以让解决这个问题变得非常简单。如果我们创建一个方法,让我们可以像编写委托一样轻松地编写表达式,这将变得非常容易。我们的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-ing 两个表达式一起使用,如果使用 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);
        }
    }
    

    【讨论】:

    • 对我来说看起来很可靠,我可能已经添加了,总体上看一下 LinqKit。它有谓词生成器等等......
    • @philsoady 我很了解 LinqKit。我更喜欢PredicateBuilder 的解决方案。它用更少的工作/代码做同样的事情。
    • @Servy 谢谢!这对我有用,我最终不需要在那里获得PredicateBuilder,我终于能够通过使用Compose 方法和替换访问者来做到:var nExpr = GroupIncludeExpression.IsNotNull().AndAlso(GetGroupMatchesIdExpresssion(_groupId));
    • @Servy,你可能知道,但不是每个人都知道。您的解决方案很好,我已经 +1 解决方案。冷静...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 2017-12-17
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多