Extend IQueryable Where() as OR instead of AND relationship

后端 未结 4 1883
有刺的猬
有刺的猬 2020-12-16 23:15

I am using my own extension methods of IQueryable<> to create chainable queries such as FindAll().FindInZip(12345).NameStartsWith(\"XYZ\").OrderByHowIWantIt() etc. which

4条回答
  •  一向
    一向 (楼主)
    2020-12-17 00:07

    In an ideal world I personally think || and && operators would be the most simple and readable. However it won't compile.

    operator ' ||' cannot be applied to operands of type 'Expression>' and 'Expression>'

    Therefore I use an extension method for this. In your example it would look like this: .Where(FindInZip(12345).Or(NameStartsWith("XYZ")).And(PostedOnOrAfter(DateTime.Now)).

    Instead of:

    .Where(FindInZip(12345) || NameStartsWith("XYZ") && (PostedOnOrAfter(DateTime.Now)).

    Expression example:

    private Expression> PostedOnOrAfter(DateTime cutoffDate)
    {
          return post => post.PostedOn >= cutoffDate;
    };
    

    Extension method:

    public  static  class PredicateExtensions
    {
         ///  
         /// Begin an expression chain
         ///  
         ///  
         ///  Default return value if the chanin is ended early
         ///  A lambda expression stub
         public  static Expression> Begin(bool value =  false)
        {
             if (value)
                 return parameter =>  true;  //value cannot be used in place of true/false
    
             return parameter =>  false;
        }
    
         public  static Expression> And(this Expression> left,
            Expression> right)
        {
             return CombineLambdas(left, right, ExpressionType.AndAlso);
        }
    
         public  static Expression> Or(this Expression> left, Expression> right)
        {
             return CombineLambdas(left, right, ExpressionType.OrElse);
        }
    
         #region private
    
         private  static Expression> CombineLambdas(this Expression> left,
            Expression> right, ExpressionType expressionType)
        {
             //Remove expressions created with Begin()
             if (IsExpressionBodyConstant(left))
                 return (right);
    
            ParameterExpression p = left.Parameters[0];
    
            SubstituteParameterVisitor visitor =  new SubstituteParameterVisitor();
            visitor.Sub[right.Parameters[0]] = p;
    
            Expression body = Expression.MakeBinary(expressionType, left.Body, visitor.Visit(right.Body));
             return Expression.Lambda>(body, p);
        }
    
         private  static  bool IsExpressionBodyConstant(Expression> left)
        {
             return left.Body.NodeType == ExpressionType.Constant;
        }
    
         internal  class SubstituteParameterVisitor : ExpressionVisitor
        {
             public Dictionary Sub =  new Dictionary();
    
             protected  override Expression VisitParameter(ParameterExpression node)
            {
                Expression newValue;
                 if (Sub.TryGetValue(node,  out newValue))
                {
                     return newValue;
                }
                 return node;
            }
        }
    
         #endregion
    } 
    

    A really good article about LINQ Queries by Extending Expressions. Also the source of the extension method that I use.

    https://www.red-gate.com/simple-talk/dotnet/net-framework/giving-clarity-to-linq-queries-by-extending-expressions/

提交回复
热议问题