程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> EntityFramework動態組合Lambda表達式作為數據篩選條件,代替拼接SQL語句

EntityFramework動態組合Lambda表達式作為數據篩選條件,代替拼接SQL語句

編輯:C#入門知識

傳統的操作數據庫方式,篩選數據需要用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可以很好的完成這步操作。拿個別人現成的例子來用:

 

[csharp]  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); 
        } 
    } 

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


至此表達式順利拼接完成。當然這樣使用還是麻煩,借用別人的擴展類稍微修改一下:


[csharp] 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); 
    } 

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

 


 
完美的動態拼接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();
            }


下面是一個我自己使用的例子,僅供參考:

[csharp]  using (var db = new SHTrackerDbContext()) 
            { 
 
                var predicate = PredicateBuilder.True<Course>(); 
                settings = DecorateSettings(settings); 
 
                Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID); 
                if (!string.IsNullOrEmpty(settings.Quater_Year)) 
                { 
 
                    checkCourse = 
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any( 
                            s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year))); 
                } 
                if (settings.QuaterYearArray != null) 
                { 
                    checkCourse = 
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any( 
                            s2c => 
                            s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year)))); 
                } 
 
                if (!string.IsNullOrEmpty(settings.DPU_ID)) 
                { 
                    checkCourse = 
                        checkCourse.And( 
                            c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID))); 
                } 
                predicate = predicate.And(checkCourse); 
 
                if (settings.IsCheckInstructorName) 
                { 
                    predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName)); 
                } 
                if (!string.IsNullOrEmpty(settings.Term)) 
                { 
                    predicate = predicate.And(c => c.TermDescription.Equals(settings.Term)); 
                } 
                if (settings.TermArray != null) 
                { 
                    predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription))); 
                } 
                if (settings.CourseType != CourseType.All) 
                { 
                    predicate = predicate.And(c => c.Type == (int) settings.CourseType); 
                } 
                var cc = 
                    new CourseCollection( 
                        db.Courses.AsNoTracking() 
                          .Where(predicate) 
                          .OrderByDescending(m => m.ID) 
                          .Skip((pageIndex - 1)*pageSize) 
                          .Take(pageSize) 
                          .ToList(), 
                        db.Courses.AsNoTracking().Where(predicate).Count()) 
                        { 
                            PageIndex = pageIndex, 
                            PageSize = pageSize, 
                            Settings = DecorateSettings(settings) 
                        }; 
 
                return cc; 
            } 

using (var db = new SHTrackerDbContext())
            {

                var predicate = PredicateBuilder.True<Course>();
                settings = DecorateSettings(settings);

                Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID);
                if (!string.IsNullOrEmpty(settings.Quater_Year))
                {

                    checkCourse =
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
                            s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));
                }
                if (settings.QuaterYearArray != null)
                {
                    checkCourse =
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
                            s2c =>
                            s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year))));
                }

                if (!string.IsNullOrEmpty(settings.DPU_ID))
                {
                    checkCourse =
                        checkCourse.And(
                            c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));
                }
                predicate = predicate.And(checkCourse);

                if (settings.IsCheckInstructorName)
                {
                    predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName));
                }
                if (!string.IsNullOrEmpty(settings.Term))
                {
                    predicate = predicate.And(c => c.TermDescription.Equals(settings.Term));
                }
                if (settings.TermArray != null)
                {
                    predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription)));
                }
                if (settings.CourseType != CourseType.All)
                {
                    predicate = predicate.And(c => c.Type == (int) settings.CourseType);
                }
                var cc =
                    new CourseCollection(
                        db.Courses.AsNoTracking()
                          .Where(predicate)
                          .OrderByDescending(m => m.ID)
                          .Skip((pageIndex - 1)*pageSize)
                          .Take(pageSize)
                          .ToList(),
                        db.Courses.AsNoTracking().Where(predicate).Count())
                        {
                            PageIndex = pageIndex,
                            PageSize = pageSize,
                            Settings = DecorateSettings(settings)
                        };

                return cc;
            }

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved