【问题标题】:Expression tree and AND condition表达式树和 AND 条件
【发布时间】:2013-02-24 03:45:57
【问题描述】:

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

我编写了以下扩展来根据所选过滤器参数动态构建表达式:

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;
}

我是这样使用的:

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

if(MyFilter.SomeParam) {
    expression = expression.And(x=>x.MyProperty == MyFilter.SomeParam);
}

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

LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。

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

有什么简单的方法可以让这段代码与实体框架一起工作吗?

【问题讨论】:

    标签: c# filter conditional-statements expression-trees


    【解决方案1】:

    如果你总是想“与”你的谓词,你不需要弄乱表达式树,只需为每个谓词添加额外的 where 子句。见http://www.thinqlinq.com/Default/Dynamically-extending-LINQ-queryies-without-building-expression-trees.aspx

    【讨论】:

      【解决方案2】:

      请在下面的博文中找到解决方案:

      A universal PredicateBuilder

      这篇博文的作者提供了另一种“Add”方法的实现来结合2个表达式。

      /// <summary>
      /// Enables the efficient, dynamic composition of query predicates.
      /// </summary>
      public static class PredicateBuilder
      {
          /// <summary>
          /// Creates a predicate that evaluates to true.
          /// </summary>
          public static Expression<Func<T, bool>> True<T>() { return param => true; }
      
          /// <summary>
          /// Creates a predicate that evaluates to false.
          /// </summary>
          public static Expression<Func<T, bool>> False<T>() { return param => false; }
      
          /// <summary>
          /// Creates a predicate expression from the specified lambda expression.
          /// </summary>
          public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
      
          /// <summary>
          /// Combines the first predicate with the second using the logical "and".
          /// </summary>
          public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
          {
              return first.Compose(second, Expression.AndAlso);
          }
      
          /// <summary>
          /// Combines the first predicate with the second using the logical "or".
          /// </summary>
          public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
          {
              return first.Compose(second, Expression.OrElse);
          }
      
          /// <summary>
          /// Negates the predicate.
          /// </summary>
          public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
          {
              var negated = Expression.Not(expression.Body);
              return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
          }
      
          /// <summary>
          /// Combines the first expression with the second using the specified merge function.
          /// </summary>
          static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
          {
              // zip parameters (map from parameters of second to parameters of first)
              var map = first.Parameters
                  .Select((f, i) => new { f, s = second.Parameters[i] })
                  .ToDictionary(p => p.s, p => p.f);
      
              // replace parameters in the second lambda expression with the parameters in the first
              var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
      
              // create a merged lambda expression with parameters from the first expression
              return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
          }
      
          class ParameterRebinder : ExpressionVisitor
          {
              readonly Dictionary<ParameterExpression, ParameterExpression> map;
      
              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);
              }
          }
      }
      

      【讨论】:

      • “仅链接”的答案在这里往往不受欢迎。您能否至少提供一个摘要或其他说明为什么它会有用。还有一种可能是,当人们稍后阅读您的答案时,您链接的任何内容都可能不再可用。
      【解决方案3】:

      Domain Oriented N-Layered .NET 4.0 Sample App 尝试这个实现(也有specification pattern 的实现):

      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);
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多