動態LINQ(Lambda表達式)構建


using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NUnit.Framework;

namespace ConsoleApplication1
{
    /// <summary>
    /// 動態Linq
    /// </summary>
    [TestFixture]
    public class LinqTest
    {
      
        [Test]
        public void Test()
        {
            #region 動態Linq(動態lambda表達式構建)

            QueryCondition queryCondition = new QueryCondition(null, "動態測試", new DateTime(2013, 1, 1), new DateTime(2013, 3, 1), null, null, 0, true);
            QueryCondition queryCondition2 = new QueryCondition(null, string.Empty, null, null, null, 60, 1, false);

            QueryCondition queryCondition3 = new QueryCondition(null, string.Empty, null, new DateTime(2013, 5, 1), null, 60, 5, true);

            QueryCondition[] queryConditionLs = new QueryCondition[] { queryCondition, queryCondition2, queryCondition3 };
            DynamicLambda dynamicLinq = new DynamicLambda();

            List<TestUser> queryLs;
            queryLs = dynamicLinq.GetTestData();
            Console.WriteLine("原始測試數據有{0}條,如下\n", queryLs.Count);
            dynamicLinq.PrintResult(queryLs);

            Console.WriteLine("---------------查詢分隔符------------------\n");

            //queryLs = dynamicLinq.GetDataByGeneralQuery(queryConditionLs[0]);
            queryLs = dynamicLinq.GetDataByDynamicQuery(queryConditionLs[0]);
            Console.WriteLine("滿足查詢結果的數據有{0}條,如下\n", queryLs.Count);
            dynamicLinq.PrintResult(queryLs);

            #endregion

            Console.ReadLine();
        }

        /// <summary>
        /// 動態Lambda表達式類
        /// </summary>
        public class DynamicLambda
        {
            public Dictionary<string, string> queryDict = new Dictionary<string, string>();
            public OrderEntry[] orderEntries;

            public DynamicLambda()
            {
                InitDynamicQueryMapping();
                orderEntries = new OrderEntry[] {
                new OrderEntry(){OrderStr="Id",OrderType=typeof(int)},
                new OrderEntry(){OrderStr="Name",OrderType=typeof(string)},
                new OrderEntry(){OrderStr="Birth",OrderType=typeof(string)},
                new OrderEntry(){OrderStr="IsStudent",OrderType=typeof(string)},
                new OrderEntry(){OrderStr="Cellphone",OrderType=typeof(string)},
                new OrderEntry(){OrderStr="Email",OrderType=typeof(string)},
                new OrderEntry(){OrderStr="Score",OrderType=typeof(int)}
            };
            }

            /*在一般的業務環境中我們常常會遇到動態查詢的情況,對於以前純T-SQL情況下我們一般是采用根據相應條件動態拼接相應的where條件上去達到相應效果
             如今在這個LINQ橫行的年代,怎么能利用LINQ完成動態查詢呢
             */

            #region 一般的解決方案

            //如果要根據對應的Linq的方式怎么完成
            public List<TestUser> GetDataByGeneralQuery(QueryCondition queryCondition)
            {
                //此處一般會從數據庫或者其他地方獲取到業務所用到的數據源
                List<TestUser> sourceLs = GetTestData();

                /*根據不同情況添加不同查詢條件,但是我們都摘掉平時開發中需求是不斷變化的,怎么能更好的應對PM各種扭曲的要求爾不必一次一次的添加各種if條件呢
                萬一有一天,PM要求你將某些條件合並例如名字和ID變為OR的關系怎么辦,如果需要利用LINQ進行動態的排序怎么辦,或者如果過濾的名字是一個
                不定的字符串數組怎么辦,這些都是我們經常會遇到的,我們不能因為每次這樣的改動而去修改這里的東西, 而且有的時候我們知道在Where(n=>n.?==?)
                但是編譯器是不知道的,這是我們就要用到動態lambda表達式(動態linq的方式)
                */
                if (queryCondition.QueryId.HasValue)
                    sourceLs = sourceLs.Where(n => n.Id == queryCondition.QueryId).ToList<TestUser>();
                if (!string.IsNullOrEmpty(queryCondition.QueryName))
                    sourceLs = sourceLs.Where(n => n.Name.ToLower().Contains(queryCondition.QueryName.ToLower())).ToList<TestUser>();
                if (queryCondition.QueryStartTime.HasValue)
                    sourceLs = sourceLs.Where(n => n.Birth >= queryCondition.QueryStartTime.Value).ToList<TestUser>();
                if (queryCondition.QueryEndTime.HasValue)
                    sourceLs = sourceLs.Where(n => n.Birth < queryCondition.QueryEndTime.Value).ToList<TestUser>();
                if (queryCondition.QueryBoolean != null)
                    sourceLs = sourceLs.Where(n => n.IsStudent = queryCondition.QueryBoolean.Value).ToList<TestUser>();
                if (queryCondition.QueryScore.HasValue)
                    sourceLs = sourceLs.Where(n => n.Score == queryCondition.QueryScore.Value).ToList<TestUser>();

                switch (queryCondition.OrderField)
                {
                    case 0:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Id).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Id).ToList<TestUser>();
                        }; break;
                    case 1:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Name).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Name).ToList<TestUser>();
                        }; break;
                    case 2:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Birth).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Birth).ToList<TestUser>();
                        }; break;
                    case 3:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.IsStudent).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.IsStudent).ToList<TestUser>();
                        }; break;
                    case 4:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Cellphone).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Cellphone).ToList<TestUser>();
                        }; break;
                    case 5:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Email).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Email).ToList<TestUser>();
                        }; break;
                    case 6:
                        {
                            if (queryCondition.IsDesc)
                                sourceLs = sourceLs.OrderByDescending(n => n.Score).ToList<TestUser>();
                            else
                                sourceLs = sourceLs.OrderBy(n => n.Score).ToList<TestUser>();
                        }; break;
                    default:
                        break;
                }

                return sourceLs;
            }

            #endregion

            #region 動態構建Lambda表達式-動態Expression樹

            public List<TestUser> GetDataByDynamicQuery(QueryCondition queryCondition)
            {
                IQueryable<TestUser> sourceLs = GetTestData().AsQueryable<TestUser>();
                string[] orderParams = new string[] { "OrderField", "IsDesc" };

                Expression filter;
                Expression totalExpr = Expression.Constant(true);

                ParameterExpression param = Expression.Parameter(typeof(TestUser), "n");
                Type queryConditionType = queryCondition.GetType();
                foreach (PropertyInfo item in queryConditionType.GetProperties())
                {
                    //反射找出所有查詢條件的屬性值,如果該查詢條件值為空或者null不添加動態lambda表達式
                    string propertyName = item.Name;
                    var propertyVal = item.GetValue(queryCondition, null);

                    if (!orderParams.Contains(propertyName) && propertyVal != null && propertyVal.ToString() != string.Empty)
                    {
                        //n.property
                        Expression left = Expression.Property(param, typeof(TestUser).GetProperty(queryDict[propertyName]));
                        //等式右邊的值
                        Expression right = Expression.Constant(propertyVal);
                        //此處如果有特殊的判斷可以自行修改例如要是Contain的,要是時間大於小於的這種判斷, 這里也可以用類似InitDynamicQueryMapping方法進行表驅動維護

                        if (propertyName == "QueryStartTime")
                            filter = Expression.GreaterThanOrEqual(left, right);
                        else if (propertyName == "QueryEndTime")
                            filter = Expression.LessThan(left, right);
                        else if (propertyName == "QueryName")
                            filter = Expression.Call(Expression.Property(param, typeof(TestUser).GetProperty(queryDict[propertyName])), typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(propertyVal));
                        else
                            filter = Expression.Equal(left, right);

                        totalExpr = Expression.And(filter, totalExpr);
                    }
                }
                //Where部分條件
                Expression pred = Expression.Lambda(totalExpr, param);
                Expression whereExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { typeof(TestUser) }, Expression.Constant(sourceLs), pred);
                //OrderBy部分排序
                MethodCallExpression orderByCallExpression = Expression.Call(typeof(Queryable), queryCondition.IsDesc ? "OrderByDescending" : "OrderBy", new Type[] { typeof(TestUser), orderEntries[queryCondition.OrderField].OrderType }, whereExpression, Expression.Lambda(Expression.Property(param, orderEntries[queryCondition.OrderField].OrderStr), param));

                //生成動態查詢
                sourceLs = sourceLs.Provider.CreateQuery<TestUser>(orderByCallExpression);

                return sourceLs.ToList<TestUser>();
            }

            #endregion

            /// <summary>
            /// 具體查詢屬性和實體里面屬性作Mapping,當然你可以對名字規范做一個顯示那樣不用做映射用反射獲取到直接構建表達式也行
            /// 具體這里只是假設模擬了一個這種情況,使用者可以根據自身業務情況適當修改
            /// </summary>
            public void InitDynamicQueryMapping()
            {
                //查詢mapping
                queryDict.Add("QueryId", "Id");
                queryDict.Add("QueryName", "Name");
                queryDict.Add("QueryStartTime", "Birth");
                queryDict.Add("QueryEndTime", "Birth");
                queryDict.Add("QueryBoolean", "IsStudent");
                queryDict.Add("QueryScore", "Score");
            }

            /// <summary>
            /// 制造測試數據
            /// </summary>
            /// <returns></returns>
            public List<TestUser> GetTestData()
            {
                List<TestUser> testLs = new List<TestUser>();
                testLs.AddRange(new TestUser[] {
                new TestUser() { Id=1, Name="測試1", Birth=new DateTime(2013,1,1), IsStudent=true, Cellphone="123456789", Email="test001@qq.com", Score=100 },
                new TestUser() { Id=2, Name="測試2", Birth=new DateTime(2013,1,2), IsStudent=false, Cellphone="23123513", Email="test002@qq.com", Score=60 },
                new TestUser() { Id=3, Name="測試3", Birth=new DateTime(2013,1,3), IsStudent=true, Cellphone="36365656", Email="test003@qq.com", Score=98 },
                new TestUser() { Id=4, Name="測試4", Birth=new DateTime(2013,1,4), IsStudent=false, Cellphone="23423525", Email="test004@qq.com", Score=86 },
                new TestUser() { Id=5, Name="測試5", Birth=new DateTime(2013,1,5), IsStudent=true, Cellphone="9867467", Email="test006@qq.com", Score=96 },
                new TestUser() { Id=6, Name="測試6", Birth=new DateTime(2013,1,6), IsStudent=false, Cellphone="536546345", Email="test007@qq.com", Score=99 },
                new TestUser() { Id=7, Name="測試7", Birth=new DateTime(2013,1,7), IsStudent=true, Cellphone="45234552", Email="test008@qq.com", Score=98 },
                new TestUser() { Id=8, Name="測試8", Birth=new DateTime(2013,1,8), IsStudent=false, Cellphone="536375636", Email="test009@qq.com", Score=97 },
                new TestUser() { Id=9, Name="測試9", Birth=new DateTime(2013,2,1), IsStudent=true, Cellphone="123456789", Email="test010@qq.com", Score=88 },
                new TestUser() { Id=10, Name="測試10", Birth=new DateTime(2013,2,2), IsStudent=false, Cellphone="4524245", Email="test011@qq.com", Score=88 },
                new TestUser() { Id=11, Name="動態測試11", Birth=new DateTime(2013,2,3), IsStudent=false, Cellphone="64767484", Email="test012@qq.com", Score=87 },
                new TestUser() { Id=12, Name="動態測試12", Birth=new DateTime(2013,2,4), IsStudent=true, Cellphone="78578568", Email="test013@qq.com", Score=86 },
                new TestUser() { Id=13, Name="動態測試13", Birth=new DateTime(2013,2,5), IsStudent=false, Cellphone="123456789", Email="test014@qq.com", Score=60 },
                new TestUser() { Id=14, Name="動態測試14", Birth=new DateTime(2013,2,6), IsStudent=true, Cellphone="123456789", Email="test015@qq.com", Score=60 },
                new TestUser() { Id=15, Name="動態測試15", Birth=new DateTime(2013,2,7), IsStudent=false, Cellphone="123456789", Email="test016@qq.com", Score=59 },
                new TestUser() { Id=16, Name="動態測試16", Birth=new DateTime(2013,2,8), IsStudent=true, Cellphone="34135134", Email="test017@qq.com", Score=58 },
                new TestUser() { Id=17, Name="動態測試17", Birth=new DateTime(2013,3,1), IsStudent=false, Cellphone="123456789", Email="test018@qq.com", Score=100 },
                new TestUser() { Id=18, Name="動態測試18", Birth=new DateTime(2013,3,2), IsStudent=true, Cellphone="34165451234", Email="test019@qq.com", Score=86 },
                new TestUser() { Id=19, Name="動態測試19", Birth=new DateTime(2013,3,3), IsStudent=false, Cellphone="462645246", Email="test020@qq.com", Score=64 },
                new TestUser() { Id=20, Name="動態測試20", Birth=new DateTime(2013,3,4), IsStudent=true, Cellphone="61454343", Email="test021@qq.com", Score=86 },
            });
                return testLs;
            }

            /// <summary>
            /// 打印測試數據
            /// </summary>
            /// <param name="resultLs"></param>
            public void PrintResult(List<TestUser> resultLs)
            {
                foreach (TestUser item in resultLs)
                {
                    Console.WriteLine("序號:{0},姓名:{1},生日:{2},是否在讀:{3},聯系手機:{4},郵箱:{5},分數:{6}", item.Id, item.Name, item.Birth, item.IsStudent ? "" : "", item.Cellphone, item.Email, item.Score);
                }
            }
        }

        /// <summary>
        /// 業務實體類-你可以想象成你業務中需要實際使用的類
        /// </summary>
        public class TestUser
        {
            public TestUser() { }

            public int Id { get; set; }

            public string Name { get; set; }

            public DateTime Birth { get; set; }

            public bool IsStudent { get; set; }

            public string Cellphone { get; set; }

            public string Email { get; set; }

            public int Score { get; set; }
        }

        /// <summary>
        /// 排序幫助類
        /// </summary>
        public class OrderEntry
        {
            public string OrderStr { get; set; }

            public Type OrderType { get; set; }
        }

        /// <summary>
        /// 業務查詢條件類-實際使用中你可以根據自己的需要構建你的查詢條件類
        /// </summary>
        public class QueryCondition
        {
            public QueryCondition() { }

            public QueryCondition(int? queryId, string queryName, DateTime? queryStart, DateTime? queryEnd, bool? queryBoolean, int? queryScore, int orderField, bool isDesc)
            {
              QueryId = queryId;
              QueryName = queryName;
              QueryStartTime = queryStart;
              QueryEndTime = queryEnd;
              QueryBoolean = queryBoolean;
              QueryScore = queryScore;
              OrderField = orderField;
              IsDesc = isDesc;
            }

            public int? QueryId { get; set; }

            public string QueryName { get; set; }

            public DateTime? QueryStartTime { get; set; }

            public DateTime? QueryEndTime { get; set; }

            public bool? QueryBoolean { get; set; }

            public int? QueryScore { get; set; }

            public int OrderField { get; set; }

            public bool IsDesc { get; set; }
        }
    }
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM