.NetCore 使用 Linq 动态拼接Expression表达式条件来实现 对EF、EF Core 扩展查询排序操作

龙码精神 2020/5/6 23:39:20

相信在使用EF的时候对查询条件或者排序上的处理令人心烦,下面我们就来动态拼接表达式解决这一问题 当我们在查询中使用Where的时候可以看到如下参数 下面我们就来扩展 Expression<Func<T,bool>> 这个参数 第一步: 建立处理功能类 首先我们要创建一个查询条件转化为表达式的泛型功能

相信在使用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 </span>= Expression.Constant(<span style="color: #0000ff;">int</span>.Parse(valueArr[<span style="color: #800080;">1</span><span style="color: #000000;">]));
        Expression end </span>=<span style="color: #000000;"> Expression.GreaterThanOrEqual(key, Expression.Convert(endvalue, key.Type));
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> Expression.AndAlso(start, end);
    }</span></pre>

   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);
            </span><span style="color: #0000ff;">var</span> <span style="color: #0000ff;">orderby</span> = Expression.Lambda&lt;Func&lt;T, <span style="color: #0000ff;">object</span>&gt;&gt;<span style="color: #000000;">(propertySelector, parameter);
            </span><span style="color: #0000ff;">if</span> (orderinfo.Order ==<span style="color: #000000;"> OrderSequence.DESC)
                query </span>= query.OrderByDescending(<span style="color: #0000ff;">orderby</span><span style="color: #000000;">);
            </span><span style="color: #0000ff;">else</span><span style="color: #000000;">
                query </span>= query.OrderBy(<span style="color: #0000ff;">orderby</span><span style="color: #000000;">);

        }
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> query;
    }</span></pre>

扩展分页

 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")

 

随时随地学软件编程-关注百度小程序和微信小程序
关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[.NetCore 使用 Linq 动态拼接Expression表达式条件来实现 对EF、EF Core 扩展查询排序操作]http://www.zyiz.net/tech/detail-133203.html

上一篇:IdentityServer4结合AspNetCore.Identity实现登录认证踩坑填坑记录

下一篇:.NetCore 实现分页控件(URL分页)实战

赞(0)

共有 条评论 网友评论

验证码: 看不清楚?
    关注微信小程序
    程序员编程王-随时随地学编程

    扫描二维码或查找【程序员编程王】

    可以随时随地学编程啦!

    技术文章导航 更多>
    扫一扫关注最新编程教程