zoukankan      html  css  js  c++  java
  • Lambda表达式动态拼接(备忘)

    EntityFramework动态组合Lambda表达式作为数据筛选条件,代替拼接SQL语句

    分类: C# Lambda/Linq Entity Framework 491人阅读 评论(0) 收藏 举报

    传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句。

    在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如:

    using(var db=new MyDbContext())

    {

    var s= db.Students.ToList().First(s=>s.ID=1200);

    }

    嘣!进行了全表数据读取!当然一般人也不会犯这种低级的错误,言归正传。

    可以简单的这样筛选数据:

    using(var db=new MyDbContext())

    {

    var list =db.Students.AsQueryable();

    if(********){list=list.Where(s=>s.ID=1200);}

    if(******){list=list.Where(...)}

    }

    但是有时这种方法不能完成特定需求,如:

    using(var db=new MyDbContext())

    {

    var list =db.Students.AsQueryable();

    if(条件1){list=list.Where(s=>s.ID>1200);}

    if(条件2){list=list.Where(s=>s.ID<1000);}

    }

    现在条件1和条件2同时成立,得到的是空结果集而不是ID>1200和ID<1000的结果集。

    这只是两个并列简单条件的组合,如果是条件嵌套呢?

    下面是假想:

    using (var db = new MyDbContext())
                {


                    Expression<Func<Student, bool>> checkStudent1 = s1 => s1.ID > 1200;
                    Expression<Func<Student, bool>> checkStudent2 = s2 => s2.ID < 1000;
                    var e =
                        Expression.Lambda<Func<Student, bool>>(
                            Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
                    var result = db.Students.Where(e).ToList();
                }

    叫它假想的原因是执行会产生异常”The parameter 's2' was not bound in the specified LINQ to Entities query expression“。

    e的内容是{s1 => ((s1.ID > 1200) Or (s2.ID < 1000))},很明显s2这个参数是没有被定义的。

    实际上我们一直操作一个Student表,最终我们想要的也是多Lambda表达式合在一起对该Student表的操作。换句话说,s2应该用s1代替。

    有人说了,这样:

    Expression<Func<Student, bool>> checkStudent1 = s => s.ID > 1200;
                    Expression<Func<Student, bool>> checkStudent2 = s => s.ID < 1000;
                    var e =
                        Expression.Lambda<Func<Student, bool>>(
                            Expression.Or(checkStudent1.Body, checkStudent2.Body), checkStudent1.Parameters);
                    var result = db.Students.Where(e).ToList();

    异常:”The parameter 's' was not bound in the specified LINQ to Entities query expression“。

    e的内容是{s => ((s.ID > 1200) Or (s.ID < 1000))},现在参数都一样是s了,但其实它们的GUID不同,也就是说它们还是两个不同的参数。

    我们需要做的是手工把checkStudent2.Body里面的参数s换成checkStudent1.Body里面的参数s。

    ExpressionVisitor可以很好的完成这步操作。拿个别人现成的例子来用:

    1. public class ParameterRebinder : ExpressionVisitor  
    2.     {  
    3.         private readonly Dictionary<ParameterExpression, ParameterExpression> map;  
    4.   
    5.         public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)  
    6.         {  
    7.             this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();  
    8.         }  
    9.   
    10.         public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)  
    11.         {  
    12.             return new ParameterRebinder(map).Visit(exp);  
    13.         }  
    14.   
    15.         protected override Expression VisitParameter(ParameterExpression p)  
    16.         {  
    17.             ParameterExpression replacement;  
    18.             if (map.TryGetValue(p, out replacement))  
    19.             {  
    20.                 p = replacement;  
    21.             }  
    22.             return base.VisitParameter(p);  
    23.         }  
    24.     }  
    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);
            }
        }


    更改后的测试代码:

    Expression<Func<Student, bool>> checkStudent1 = s => s.ID > 1200;
                    Expression<Func<Student, bool>> checkStudent2 = s => s.ID < 1000;
                    
                    var body2 =
                        ParameterRebinder.ReplaceParameters(
                            checkStudent2.Parameters.Select((s,i)=>new{s,f=checkStudent1.Parameters[i]}).ToDictionary(p=>p.s,p=>p.f), checkStudent2.Body);
                    var e =
                        Expression.Lambda<Func<Student, bool>>(
                            Expression.Or(checkStudent1.Body, body2), checkStudent1.Parameters);
                    var result = db.Students.Where(e).ToList();

    至此表达式顺利拼接完成。当然这样使用还是麻烦,借用别人的扩展类稍微修改一下:

    1. public static class PredicateBuilder  
    2. {  
    3.   
    4.     public static Expression<Func<T, bool>> True<T>() { return f => true; }  
    5.     public static Expression<Func<T, bool>> False<T>() { return f => false; }  
    6.     public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)  
    7.     {  
    8.         // build parameter map (from parameters of second to parameters of first)   
    9.         var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);  
    10.   
    11.         // replace parameters in the second lambda expression with parameters from the first   
    12.         var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);  
    13.   
    14.         // apply composition of lambda expression bodies to parameters from the first expression    
    15.         return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);  
    16.     }  
    17.   
    18.     public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)  
    19.     {  
    20.         return first.Compose(second, Expression.And);  
    21.     }  
    22.   
    23.     public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)  
    24.     {  
    25.         return first.Compose(second, Expression.Or);  
    26.     }  
    27. }  
        public static class PredicateBuilder
        {
    
            public static Expression<Func<T, bool>> True<T>() { return f => true; }
            public static Expression<Func<T, bool>> False<T>() { return f => false; }
            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);
            }
        }
    参考:http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
    完美的动态拼接Lambda表达式如下:

    using (var db = new MyDbContext())
                {
                    var predicate = PredicateBuilder.True<Student>();
                    predicate=predicate.And(s => s.ID > 1200);
                    predicate=predicate.Or(s => s.ID < 1000);
                    var result = db.Students.Where(predicate).ToList();
                }

    下面是一个我自己使用的例子,仅供参考:
    1. using (var db = new SHTrackerDbContext())  
    2.             {  
    3.   
    4.                 var predicate = PredicateBuilder.True<Course>();  
    5.                 settings = DecorateSettings(settings);  
    6.   
    7.                 Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID);  
    8.                 if (!string.IsNullOrEmpty(settings.Quater_Year))  
    9.                 {  
    10.   
    11.                     checkCourse =  
    12.                         c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(  
    13.                             s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));  
    14.                 }  
    15.                 if (settings.QuaterYearArray != null)  
    16.                 {  
    17.                     checkCourse =  
    18.                         c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(  
    19.                             s2c =>  
    20.                             s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year))));  
    21.                 }  
    22.   
    23.                 if (!string.IsNullOrEmpty(settings.DPU_ID))  
    24.                 {  
    25.                     checkCourse =  
    26.                         checkCourse.And(  
    27.                             c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));  
    28.                 }  
    29.                 predicate = predicate.And(checkCourse);  
    30.   
    31.                 if (settings.IsCheckInstructorName)  
    32.                 {  
    33.                     predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName));  
    34.                 }  
    35.                 if (!string.IsNullOrEmpty(settings.Term))  
    36.                 {  
    37.                     predicate = predicate.And(c => c.TermDescription.Equals(settings.Term));  
    38.                 }  
    39.                 if (settings.TermArray != null)  
    40.                 {  
    41.                     predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription)));  
    42.                 }  
    43.                 if (settings.CourseType != CourseType.All)  
    44.                 {  
    45.                     predicate = predicate.And(c => c.Type == (int) settings.CourseType);  
    46.                 }  
    47.                 var cc =  
    48.                     new CourseCollection(  
    49.                         db.Courses.AsNoTracking()  
    50.                           .Where(predicate)  
    51.                           .OrderByDescending(m => m.ID)  
    52.                           .Skip((pageIndex - 1)*pageSize)  
    53.                           .Take(pageSize)  
    54.                           .ToList(),  
    55.                         db.Courses.AsNoTracking().Where(predicate).Count())  
    56.                         {  
    57.                             PageIndex = pageIndex,  
    58.                             PageSize = pageSize,  
    59.                             Settings = DecorateSettings(settings)  
    60.                         };  
    61.   
    62.                 return cc;  
    63.             }  

    参考网址http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx

  • 相关阅读:
    【Django】Django 定时任务实现(django-crontab+command)
    【Django】Django web项目部署(Nginx+uwsgi)
    【python】pandas & matplotlib 数据处理 绘制曲面图
    Struts2的简单使用
    Servlet,jsp,JSP技术 ,JSP编程
    Servlet、JSP中页面跳转的方式
    html/京东项目/京东网页高仿/js/jq/css/java web/
    Java ResourceBundle类的使用
    sql语句executeQuery和executeUpdate之间的区别
    jdbc/DAO模式
  • 原文地址:https://www.cnblogs.com/CielWater/p/3399949.html
Copyright © 2011-2022 走看看