zoukankan      html  css  js  c++  java
  • (转)LINQ to Entities 多条件动态查询

    原文地址:http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx

    Someone asked a great question on the ADO.NET Entity Framework forums yesterday: how do I compose predicates in LINQ to Entities? I’ll give three answers to the question.

    Answer 1: Chaining query operators

    Basically, you have some query and you have some predicates you want to apply to that query (“the car is red”, “the car costs less than $10”). If both conditions need to be satisfied, you can just chain together some calls to Where (“the car is red and costs less than $10”):

    Expression<Func<Car, bool>> theCarIsRed = c => c.Color == "Red";

    Expression<Func<Car, bool>> theCarIsCheap = c => c.Price < 10.0;

    IQueryable<Car> carQuery = ;

    var query = carQuery.Where(theCarIsRed).Where(theCarIsCheap);

    If you’re willing to exceed the $10 budget for cars that are red, you can chain Unions instead (“the car is red or the car costs less than $10”):

    var query2 = carQuery.Where(theCarIsRed).Union(carQuery.Where(theCarIsCheap));

    This last query has a couple of problems: it’s inefficient (because of the unions) and it eliminates duplicates in the results, something that would not happen if I applied a single predicate.

    Answer 2: Build expressions manually

    The LINQ Expressions API includes factory methods that allow you to build up the predicate by hand. I can define the conditions (with respect to a “car” parameter) as follows:

    ParameterExpression c = Expression.Parameter(typeof(Car), "car");

    Expression theCarIsRed = Expression.Equal(Expression.Property(c, "Color"), Expression.Constant("Red"));

    Expression theCarIsCheap = Expression.LessThan(Expression.Property(c, "Price"), Expression.Constant(10.0));

    Expression<Func<Car, bool>> theCarIsRedOrCheap = Expression.Lambda<Func<Car, bool>>(

        Expression.Or(theCarIsRed, theCarIsCheap), c);

    var query = carQuery.Where(theCarIsRedOrCheap);

    Building queries by hand isn’t very convenient. If you’re already building expressions from scratch, this is a good approach but otherwise I’d suggest something different…

    Answer 3: Composing Lambda Expresions

    The Albaharis suggest combining bodies of lambda expressions in their C# 3.0 book (a great resource for all things C# and LINQ). This allows you to describe the parts of the expression using the lambda syntax and build an aggregate expression:

    Expression<Func<Car, bool>> theCarIsRed = c1 => c1.Color == "Red";

    Expression<Func<Car, bool>> theCarIsCheap = c2 => c2.Price < 10.0;

    Expression<Func<Car, bool>> theCarIsRedOrCheap = Expression.Lambda<Func<Car, bool>>(

        Expression.Or(theCarIsRed.Body, theCarIsCheap.Body), theCarIsRed.Parameters.Single());

    var query = carQuery.Where(theCarIsRedOrCheap);

    I’m taking the bodies of the two conditions and Oring them in a new lambda expression. There is a subtle problem however: the parameter for the merged expression (c1) is taken from “theCarIsRed”, which leaves us with a dangling parameter (c2) from “theCarIsCheap”. The resulting query is invalid. How can I force “theCarIsCheap” to use the same parameter? The answer is to invoke the expression using the common parameter:

    ParameterExpression p = theCarIsRed.Parameters.Single();

    Expression<Func<Car, bool>> theCarIsRedOrCheap = Expression.Lambda<Func<Car, bool>>(

        Expression.Or(theCarIsRed.Body, Expression.Invoke(theCarIsCheap, p)), p);

    Here’s the problem: LINQ to Entities does not support InvocationExpressions. Rather than invoking the expression with c1, I can manually rebind the parameter. Matt Warren’s series of articles on IQueryable providers includes an ExpressionVisitor implementation that makes it easy to rewrite expression trees. If you do any LINQ expression manipulation, this class is a crucial tool. Here’s an implementation of the visitor that rebinds parameters:

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

        }

    }

    Now I can write a general utility method to compose lambda expressions without using invoke (I’ll call it Compose), and leverage it to implement EF-friendly And and Or builder methods:

    public static class Utility {

        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) {

            // build parameter 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 parameters from the first

            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

           // apply composition of lambda expression bodies to parameters from the first expression

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

        }

    }

    To combine lambda expressions, I can write:

    Expression<Func<Car, bool>> theCarIsRed = c => c.Color == "Red";

    Expression<Func<Car, bool>> theCarIsCheap = c => c.Price < 10.0;

    Expression<Func<Car, bool>> theCarIsRedOrCheap = theCarIsRed.Or(theCarIsCheap);

    var query = carQuery.Where(theCarIsRedOrCheap);

    I’ll use this last answer as an excuse to discuss variations on the visitor pattern in a future post...

  • 相关阅读:
    30、深入浅出MFC学习笔记,多线程
    29、深入浅出MFC学习笔记,多重文件和视图
    5、程序设计实践读书笔记
    6、C++ Primer 4th 笔记,标准IO库(1)
    SQL Server流程控制 7,Try...Catch 语句
    TSQL:流程控制 4,Case 语句
    SQL Server事务处理(Tansaction)与锁(Lock)
    SQL Server9,流程控制 Execute 语句(*)
    SQL Server流程控制 2,If...Else 语句
    SQL Server流程控制 1,Begin...End 语句
  • 原文地址:https://www.cnblogs.com/fcsh820/p/2224056.html
Copyright © 2011-2022 走看看