.NetCore 使用 Linq 動態拼接Expression表達式條件來實現 對EF、EF Core 擴展查詢排序操作


相信在使用EF的時候對查詢條件或者排序上的處理令人心煩,下面我們就來動態拼接表達式解決這一問題

當我們在查詢中使用Where的時候可以看到如下參數

下面我們就來擴展 Expression<Func<T,bool>> 這個參數

第一步: 建立處理功能類

首先我們要創建一個查詢條件轉化為表達式的泛型功能類 如 UosoExpressionParser<T> 至於為什么要用泛型類目的很明確就是為了適配不同的模型參數

轉化條件為表達式 那么處理一個方法來接受條件 返回表達式,條件可以按照自己的模式去設置

 public Expression<Func<T, bool>> ParserConditions(IEnumerable<UosoConditions> conditions)
        {
            //將條件轉化成表達是的Body
            var query = ParseExpressionBody(conditions);
            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }
 public class UosoConditions
    {
        /// <summary>
        /// 字段名稱
        /// </summary>
        public string Key { get; set; }
        /// <summary>
        ////// </summary>
        public string Value { get; set; }
        /// <summary>
        /// 值類型
        /// </summary>
        public string ValueType { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public UosoOperatorEnum Operator { get; set; }
    }

第二步: 條件轉表達式具體處理

具體去實現  ParseExpressionBody 條件 枚舉提供操作方式 如:(like 、 = 、!= 、>   、<  、>=  、<=  、in 、 between) 

private Expression ParseExpressionBody(IEnumerable<UosoConditions> conditions)
        {
            if (conditions == null || conditions.Count() == 0)
            {
                return Expression.Constant(true, typeof(bool));
            }
            else if (conditions.Count() == 1)
            {
                return ParseCondition(conditions.First());
            }
            else
            {
                Expression left = ParseCondition(conditions.First());
                Expression right = ParseExpressionBody(conditions.Skip(1));
                return Expression.AndAlso(left, right);
            }
        }
 private Expression ParseCondition(UosoConditions condition)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, condition.Key);
            Expression value = Expression.Constant(condition.Value);
            switch (condition.Operator)
            {
                case UosoOperatorEnum.Contains:
                    return Expression.Call(key, typeof(string).GetMethod("Contains",new Type[] { typeof(string) }), value);
                case UosoOperatorEnum.Equal:
                    return Expression.Equal(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.Greater:
                    return Expression.GreaterThan(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.GreaterEqual:
                    return Expression.GreaterThanOrEqual(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.Less:
                    return Expression.LessThan(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.LessEqual:
                    return Expression.LessThanOrEqual(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.NotEqual:
                    return Expression.NotEqual(key, Expression.Convert(value, key.Type));
                case UosoOperatorEnum.In:
                    return ParaseIn(p, condition);
                case UosoOperatorEnum.Between:
                    return ParaseBetween(p, condition);
                default:
                    throw new NotImplementedException("不支持此操作");
            }
        }

這里對 In  和between 做了特殊處理

private Expression ParaseBetween(ParameterExpression parameter, UosoConditions conditions)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, conditions.Key);
            var valueArr = conditions.Value.Split(',');
            if (valueArr.Length != 2)
            {
                throw new NotImplementedException("ParaseBetween參數錯誤");
            }
            try
            {
                int.Parse(valueArr[0]);
                int.Parse(valueArr[1]);
            }
            catch {
                throw new NotImplementedException("ParaseBetween參數只能為數字");
            }
            Expression expression = Expression.Constant(true, typeof(bool));
            //開始位置
            Expression startvalue = Expression.Constant(int.Parse(valueArr[0]));
            Expression start = Expression.GreaterThanOrEqual(key, Expression.Convert(startvalue, key.Type));

            Expression endvalue = Expression.Constant(int.Parse(valueArr[1]));
            Expression end = Expression.GreaterThanOrEqual(key, Expression.Convert(endvalue, key.Type));
            return Expression.AndAlso(start, end);
        }
  private Expression ParaseIn(ParameterExpression parameter, UosoConditions conditions)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, conditions.Key);
            var valueArr = conditions.Value.Split(',');
            Expression expression = Expression.Constant(true, typeof(bool));
            foreach (var itemVal in valueArr)
            {
                Expression value = Expression.Constant(itemVal);
                Expression right = Expression.Equal(key, Expression.Convert(value, key.Type));
               
expression = Expression.Or(expression, right); }
return expression; }

第三步: 擴展分頁、排序、查詢條件

擴展 IQueryable<T> 就OK了,下面是我擴展的查詢 排序 分頁處理

擴展查詢

public static IQueryable<T> QueryConditions<T>(this IQueryable<T> query, IEnumerable<UosoConditions> conditions)
        {
            var parser = new UosoExpressionParser<T>();
            var filter = parser.ParserConditions(conditions);
            return query.Where(filter);
        }

擴展多條件排序

 public static IQueryable<T> OrderConditions<T>(this IQueryable<T> query, IEnumerable<UosoOrderConditions> orderConditions)
        {
            foreach (var orderinfo in orderConditions)
            {
                var t = typeof(T);
                var propertyInfo = t.GetProperty(orderinfo.Key);
                var parameter = Expression.Parameter(t);
                Expression propertySelector = Expression.Property(parameter, propertyInfo);

                var orderby = Expression.Lambda<Func<T, object>>(propertySelector, parameter);
                if (orderinfo.Order == OrderSequence.DESC)
                    query = query.OrderByDescending(orderby);
                else
                    query = query.OrderBy(orderby);

            }
            return query;
        }

擴展分頁

public static IQueryable<T> Pager<T>(this IQueryable<T> query, int pageindex, int pagesize,out int itemCount)
        {
            itemCount = query.Count();
            return query.Skip((pageindex - 1) * pagesize).Take(pagesize);
        }

擴展基本完成了,接下來就是使用方式 下面是我寫的查詢分頁方式

第四步: 具體使用方式

 public IList<IdentityUser> GetPagedList2(IEnumerable<UosoConditions>  conditions,IEnumerable<UosoOrderConditions> orderConditions,int pageIndex, int pageSize,out int itemcount)
        {
            return _userManager.Users.AsNoTracking().QueryConditions(conditions).OrderConditions(orderConditions).Pager(pageIndex, pageSize, out itemcount).ToList();
        }

你需要構建相關的查詢排序集合類就行了 如下:

List<UosoConditions> uosoConditions = new List<UosoConditions>() {
                 new UosoConditions { Key = "UserName", Operator = UosoOperatorEnum.Contains, Value = "1,3", ValueType = "string" }
            };
            List<UosoOrderConditions> orderConditions = new List<UosoOrderConditions> {
                 new UosoOrderConditions{
                      Key="UserName",
                       Order = OrderSequence.DESC
                 },
                 new UosoOrderConditions{
                      Key="PhoneNumber",
                       Order = OrderSequence.DESC
                 }
            };
            int itemcount = 0;
            var list = _userServices.GetPagedList2(uosoConditions, orderConditions, pageindex, pagesize, out itemcount);

第五步:結合前端分頁樣式實現整體(之前的有介紹)

ViewBag.Option = new UosoPagerOption()
            {
                ItemCount = itemcount,
                PageSize = pagesize,
                PageIndex = pageindex,
                CountNum = 5,
                Url = Request.Path.Value,
                Query = Request.Query
            };

 

以上是實現分頁的全部過程,這里值得注意的是 在 like查詢 Contains的時候,在.NetCore中需要如下這樣寫,不然可能會出現反射多次被實例化的問題

typeof(string).GetMethod("Contains",new Type[] { typeof(string) }) 

 如果是.Net Framework 中 為如下方式 

typeof(string).GetMethod("Contains")

 


免責聲明!

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



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