Linq动态拼接Expression表达式(可多表字段判别)

本文是在这篇博文:https://www.cnblogs.com/liyouming/p/9402113.html 基础上进行的修改补充:扩展了多表的连接查询的动态拼接。

1、添加客户端和服务器端的通用模型,对于本地App也可以使用。

A、查询条件类型化

    /// <summary>
    /// 查询条件
    /// </summary>
    public class LinqSelectCondition
    {
        public LinqSelectCondition()
        {
            ParentFields = new List<string>();
        }
        /// <summary>
        /// 查询字段名称
        /// </summary>
        public string Field { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string Value { get; set; }
        /// <summary>
        /// 值类型
        /// </summary>
        public string Type { get; set; }
        /// <summary>
        /// 查询操作类型
        /// </summary>
        public LinqSelectOperator Operator { get; set; }        
        /// <summary>
        /// 父表字段集合,按先后次序,层次依次升高
        /// </summary>
        public List<string> ParentFields { get; set; }
    }//

其中用到的操作比较类型:

public enum LinqSelectOperator
    {
        Contains,
        Equal,
        Greater,
        GreaterEqual,
        Less,
        LessEqual,
        NotEqual,
        InWithEqual,  //对于多个值执行等于比较
        InWithContains,//对于多个值执行包含比较
        Between,
  }

上述用到的ParentFields是一个列表,如果是单表查询,可以不用考虑,如果需要借用父表或者父表的父表等查询字段,则依次往里面添加父键的字段名。

B、排序类

public class LinqOrderCondition
    {
        public LinqOrderCondition()
        {
            ParentFields = new List<string>();
        }
        /// <summary>
        /// 查询字段名称
        /// </summary>
        public string Field { get; set; }
        /// <summary>
        /// 排序类型
        /// </summary>
        public LinqOrderType OrderType { get; set; }
        /// <summary>
        /// 父表字段集合,按先后次序,层次依次升高
        /// </summary>
        public List<string> ParentFields { get; set; }
 }
public enum LinqOrderType
    {
        ASC,
        DESC,
 }

C、页过滤类

public class PaginationModel
    {
        /// <summary>
        /// 当前页起始号,从1开的
        /// </summary>
        public int CurrentPageIndex { get; set; }
        /// <summary>
        /// 每页的尺寸
        /// </summary>

        public int PageSize { get; set; }
  }

D、打包用于客户端和服务器的公共模型

public class LinqConditionModel
    {
        public LinqConditionModel()
        {
            SelectConditions = new List<LinqSelectCondition>();
            OrderConditions = new List<LinqOrderCondition>();
        }
        /// <summary>
        /// 选择条件集合
        /// </summary>
        public List<LinqSelectCondition> SelectConditions { get; set; }
        /// <summary>
        /// 查询条件集合
        /// </summary>
        public List<LinqOrderCondition> OrderConditions { get; set; }
        /// <summary>
        /// 页查询信息
        /// </summary>
        public PaginationModel PageInfo { get; set; }
  }

LinqConditionModel主要是用于服务器和客户端的数据交互,如果是本地App,可以简化上面的一些定义。

对于EF Core的父子表映射关系的定义有多种模式,上述的模式主要对应在子表中定义对父表的映射,父表不做任何处理。

譬如一个父表:

public class Manufacture
{
    public long ID { get; set; }
    public string Name { get; set; }
}

子表的定义如下(ManufactureID映射到父表的ID)

public class Part
{
    public long ID { get; set; }
    public string Name { get; set; }
    public long ManufactureID { get; set; }
    [ForeignKey("ManufactureID ")]
    public virtual Manufacture OfManufacture { get; set; }   
}

2、表达式拼接

    public class LinqExpressionParser<T>
    {
        public Expression<Func<T, bool>> ParserConditions(IEnumerable<LinqSelectCondition> conditions)
        {
            //将条件转化成表达式的Body
            var parameter = Expression.Parameter(typeof(T));
            var query = ParseExpressionBody(conditions, parameter);
            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }
        private Expression ParseExpressionBody(IEnumerable<LinqSelectCondition> conditions, ParameterExpression parameter)
        {
            if (conditions == null || conditions.Count() == 0)
            {
                return Expression.Constant(true, typeof(bool));
            }
            else if (conditions.Count() == 1)
            {
                return ParseCondition(conditions.First(), parameter);
            }
            else
            {
                Expression left = ParseCondition(conditions.First(), parameter);
                Expression right = ParseExpressionBody(conditions.Skip(1), parameter);
                return Expression.AndAlso(left, right); //此位置可执行其他逻辑操作
            }
        }
        //对查询条件进行处理
        private Expression ParseCondition(LinqSelectCondition condition,ParameterExpression parameter)
        {
            ParameterExpression p = parameter;
            Expression key;
            //对值进行转换处理
            object convertValue = condition.Value;
            if (condition.Type != null 
                && condition.Operator!=LinqSelectOperator.InWithContains 
                && condition.Operator != LinqSelectOperator.InWithEqual)
            {
                if (condition.Type.ToUpper() == "DATETIME")
                {
                    convertValue = System.Convert.ToDateTime(condition.Value);
                }
                else if (condition.Type.ToUpper() == "INT")
                {
                    convertValue = System.Convert.ToInt32(condition.Value);
                }
                else if (condition.Type.ToUpper() == "LONG")
                {
                    convertValue = System.Convert.ToInt64(condition.Value);
                }
                else if (condition.Type.ToUpper() == "DOUBLE")
                {
                    convertValue = System.Convert.ToDouble(condition.Value);
                }
                else if (condition.Type.ToUpper() == "BOOL")
                {
                    convertValue = System.Convert.ToBoolean(condition.Value);
                }
            }
            Expression value = Expression.Constant(convertValue);
            //参数化字段
            if(condition.ParentFields!=null&& condition.ParentFields.Count > 0)
            {
                key = p;
                foreach (var parent in condition.ParentFields)
                {
                    key = Expression.Property(key, parent);
                }
                key = Expression.Property(key, condition.Field);
            }
            else
            {
                key = Expression.Property(p, condition.Field);
            }
            switch (condition.Operator)
            {
                case LinqSelectOperator.Contains:
                    return Expression.Call(key, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value);
                case LinqSelectOperator.Equal:
                    return Expression.Equal(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.Greater:
                    return Expression.GreaterThan(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.GreaterEqual:
                    return Expression.GreaterThanOrEqual(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.Less:
                    return Expression.LessThan(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.LessEqual:
                    return Expression.LessThanOrEqual(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.NotEqual:
                    return Expression.NotEqual(key, Expression.Convert(value, key.Type));
                case LinqSelectOperator.InWithEqual:
                    return ParaseIn(key, condition,true);
                case LinqSelectOperator.InWithContains:
                    return ParaseIn(key, condition, false);
                case LinqSelectOperator.Between:
                    return ParaseBetween(key, condition);
                default:
                    throw new NotImplementedException("不支持此操作");
            }
        }
        //对查询“Between"的处理
        private Expression ParaseBetween(Expression key, LinqSelectCondition conditions)
        {
            //ParameterExpression p = parameter;
            //Expression key = Expression.Property(p, conditions.Field);
            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);
        }
        //对查询“in"的处理
        private Expression ParaseIn(Expression key, LinqSelectCondition conditions,bool isEqual)
        {
            var valueArr = conditions.Value.Split(',');
            Expression expression = Expression.Constant(false, typeof(bool));
            foreach (var itemVal in valueArr)
            {
                object conValue = itemVal;
                Type keyType = key.Type;
                if (conditions.Type?.ToUpper() == "INT")
                {
                    conValue = System.Convert.ToInt32(itemVal);
                    keyType = typeof(int);
                }
                else if (conditions.Type?.ToUpper() == "LONG")
                {
                    conValue = System.Convert.ToInt64(itemVal);
                    keyType = typeof(long);
                }
                Expression value = Expression.Constant(conValue);
                Expression right;
                if(isEqual)
                   right = Expression.Equal(key, Expression.Convert(value, keyType));
                else 
                   right = Expression.Call(key, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value);
                expression = Expression.Or(expression, right);
            }
            return expression;
        }
        
    }//

上面的方法ParseExpressionBody()最后的一段可以根据逻辑执行其他的条件或等拼接方式,但因为用得少,和原文一样也只有条件与,如果有谁想扩展多种拼接方式,可以修改此处。

3、Linq表达式扩展

    public static class LinqExpressionExtended
    {
        //扩展查询
        public static IQueryable<T> QueryConditions<T>(this IQueryable<T> query, IEnumerable<LinqSelectCondition> conditions)
        {
            var parser = new LinqExpressionParser<T>();
            var filter = parser.ParserConditions(conditions);
            return query.Where(filter);
        }
        //扩展多条件排序
        public static IQueryable<T> OrderConditions<T>(this IQueryable<T> query, IEnumerable<LinqOrderCondition> orderConditions)
        {
            foreach (var orderinfo in orderConditions)
            {
                var parameter = Expression.Parameter(typeof(T));
                Expression propertySelector= parameter;
                if (orderinfo.ParentFields != null && orderinfo.ParentFields.Count > 0)
                {
                    foreach (var parent in orderinfo.ParentFields)
                    {
                        propertySelector = Expression.Property(propertySelector, parent);
                    }
                    propertySelector = Expression.Property(propertySelector, orderinfo.Field);
                }
                else
                {
                    propertySelector = Expression.Property(propertySelector, orderinfo.Field);
                }
                if (propertySelector.Type == typeof(long)) //int64不能直接转换为object
                {
                    var orderby = Expression.Lambda<Func<T, long>>(propertySelector, parameter);
                    //var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);
                    //var orderby = Expression.Lambda(funcType,propertySelector, parameter);
                    if (orderinfo.OrderType == LinqOrderType.DESC)
                        query = query.OrderByDescending(orderby);
                    else
                        query = query.OrderBy(orderby);
                }
                else if (propertySelector.Type == typeof(int)) //int64不能直接转换为object
                {
                    var orderby = Expression.Lambda<Func<T, int>>(propertySelector, parameter);
                    //var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);
                    //var orderby = Expression.Lambda(funcType,propertySelector, parameter);
                    if (orderinfo.OrderType == LinqOrderType.DESC)
                        query = query.OrderByDescending(orderby);
                    else
                        query = query.OrderBy(orderby);
                }
                else if (propertySelector.Type == typeof(DateTime))
                {
                    var orderby = Expression.Lambda<Func<T, DateTime>>(propertySelector, parameter);
                    if (orderinfo.OrderType == LinqOrderType.DESC)
                        query = query.OrderByDescending(orderby);
                    else
                        query = query.OrderBy(orderby);
                }
                else if (propertySelector.Type == typeof(TimeSpan))
                {
                    var orderby = Expression.Lambda<Func<T, TimeSpan>>(propertySelector, parameter);
                    if (orderinfo.OrderType == LinqOrderType.DESC)
                        query = query.OrderByDescending(orderby);
                    else
                        query = query.OrderBy(orderby);
                }
                else
                {
                    var orderby = Expression.Lambda<Func<T, object>>(propertySelector, parameter);
                    if (orderinfo.OrderType == LinqOrderType.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)
        {
            if (pageindex == null || pagesize == null) return query;
            return query.Skip(((int)pageindex - 1) * (int)pagesize).Take((int)pagesize);
        }
        //扩展页统计
        public async static Task<int> PageCountAsync<T>(this IQueryable<T> query, int pagesize)
        {
            return (int)Math.Ceiling((double)(await query.CountAsync() / pagesize)); 
        }
       
    }//

注意上述在对OrderConditions进行扩展时,string类型可以直接对应object,而像long之类的类型需要进行类型转换,或者如上述的判别后直接将泛型类型固定,否则运行会报错,对于“Pager”扩展,使用了可空的"int",目的是传递时如果未对PaginationModel赋值,即使调用了Pager,也不会进行分页查询,这样简化了查询判断。

4、使用

表内字段的条件判别如下:

        LinqConditionModel conditions = new LinqConditionModel();
            if (!string.IsNullOrEmpty(SearchName))
            {
                conditions.SelectConditions.Add(new LinqSelectCondition()
                {
                    Field = "Name",
                    Operator = LinqSelectOperator.Contains,
                    Value = SearchName,
                });
          }

如果要引用父表的字段,如下:

      if (!string.IsNullOrEmpty(SearchManufacture))
            {
                var con = new LinqSelectCondition()
                {
                    Field = "Name",
                    Operator = LinqSelectOperator.InWithContains,
                    Value = SearchManufacture,
                };
                con.ParentFields.Add("OfManufacture");
                conditions.SelectConditions.Add(con);
         }

        如果还要引用父表的父表,则继续添加字段到con.ParentFields,此时的Field属性对应最终的父表内的相应字段名称。比较符InWithContains可以让用户输入属性SearchManufacture的值类似为sie,sch,则会搜索厂家中Name字段中包含sie或者sch的数据,对于字段为[string]类型,可以不指定查询字段类型 [Type] ,但对于其他类型,则需要指定。

以上内容可以适用于多表取条件,查询单表数据的方式,如果要获取相关联的父表数据,简单的使用Include()即可搞定。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值