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...

  • 相关阅读:
    解决:ImportError: cannot import name 'login' from 'django.contrib.auth.views'
    报错:No module named 'django.contrib.staticfiles.templatetags'
    模块django.forms.forms的用法
    cannot import name 'python_2_unicode_compatible'
    解决ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'问题
    Django自学之 django基本命令,Django常用命令
    django使用cmd的基本命令-启动、新建
    解决 The repository located at pypi.doubanio.com is not a trusted or secure host and is being ignored.的问题
    解决ImportError: cannot import name 'six' from 'django.utils'
    设计模式--开篇
  • 原文地址:https://www.cnblogs.com/fcsh820/p/2224056.html
Copyright © 2011-2022 走看看