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

  • 相关阅读:
    高级前端工程师面试必备(持续更新中)
    用node编写cli工具
    用vscode开发vue应用
    jsbridge的js封装
    react-create-app 构建react项目的流程以及需要注意的地方
    【面试篇】寒冬求职季之你必须要懂的原生JS(中)
    基于vue-cli3.0构建功能完善的移动端架子,主要功能包括
    @vue/cl构建得项目下,postcss.config.js配置,将px转化成rem
    eslint prettier editrorconfig
    数据结构题集--集锦
  • 原文地址:https://www.cnblogs.com/fcsh820/p/2224056.html
Copyright © 2011-2022 走看看