动态拼接LINQ查询条件的解决方案

来源:互联网 发布:淘宝代销退货流程 编辑:程序博客网 时间:2024/05/01 10:33

开发中,需要根据用户选择的过滤条件从数据库中查询数据。过滤条件包括等于(”==”)、不等于(“!=”)、大于(“>”)、小于(”<”)、大于等于(“>=”)、小于等于(“<=”)、包含(”in”)、不包含(”out”)八种。过滤条件之间可以是“AND”和“OR”两种关系。过滤条件支持的数据类型有整型和字符串等。用户选择的过滤条件通过json数据提交给后端,后端反序列化后得到过滤条件对象,该对象是FilterModel类的实例,下面是FilterModel类的定义。

    public class FilterModel    {        public string column { get; set; }//过滤条件中使用的数据列        public string action { get; set; }//过滤条件中的操作:==、!=等        public string logic { get; set; }//过滤条件之间的逻辑关系:AND和OR        public string value { get; set; }//过滤条件中的操作的值        public string dataType { get; set; }//过滤条件中的操作的字段的类型    }

数据库操作使用ADO.NET EF技术,查询使用Linq。而Linq的扩展方法接收的是一个lambda表达式作为查询条件。那么怎样可以将用户选择的查询条件转换成需要的lambda表达式呢?本文给出了自己的解决方案。
首先,定义下面的泛型方法GetFilterExpression,该方法完成lambda表达式的拼接。GetFilterExpression方法中泛型参数T为需要操作的数据库表对象,输入参数为FilterModel类的实例集合,包含用户选择的所有条件,遍历该集合,调用CreateLambda()方法生成条件对应的lambda表达式,虽有根据条件之间的关系是“AND”还是“OR”完成lambda表达式的拼接工作。

        public static Expression<Func<T, bool>> GetFilterExpression<T>(List<FilterModel> filterConditionList)        {            Expression<Func<T, bool>> condition = null;            try            {                    if (filterConditionList != null && filterConditionList.Count > 0)                    {                                                foreach (FilterModel filterCondition in filterConditionList)                        {                            Expression<Func<T, bool>> tempCondition = CreateLambda<T>(filterCondition);                            if (condition == null)                            {                                condition = tempCondition;                            }                            else                            {                                if ("AND".Equals(filterCondition.logic))                                {                                    condition = condition.And(tempCondition);                                }                                else                                {                                    condition = condition.Or(tempCondition);                                }                            }                        }                    }            }            catch (Exception ex)            {                LogHelper.WriteLogException("获取筛选条件异常:" + ex.Message);            }            return condition;        }

接下来,介绍CreateLambda()方法的逻辑,该方法完成过滤条件到lambda表达式的生成操作。

        public static Expression<Func<T, bool>> CreateLambda<T>(FilterModel filterCondition)        {            var parameter = Expression.Parameter(typeof(T), "p");//创建参数i            var constant = Expression.Constant(filterCondition.value);//创建常数            MemberExpression member = Expression.PropertyOrField(parameter, filterCondition.column);            if ("=".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.Equal(member, constant), parameter);            }            else if ("!=".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.NotEqual(member, constant), parameter);            }            else if (">".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(member, constant), parameter);            }            else if ("<".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.LessThan(member, constant), parameter);            }            else if (">=".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(member, constant), parameter);            }            else if ("<=".Equals(filterCondition.action))            {                return Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(member, constant), parameter);            }            else if ("in".Equals(filterCondition.action) && "1".Equals(filterCondition.dataType))            {                return GetExpressionWithMethod<T>("Contains", filterCondition);            }            else if ("out".Equals(filterCondition.action) && "1".Equals(filterCondition.dataType))            {                return GetExpressionWithoutMethod<T>("Contains", filterCondition);            }            else            {                return null;            }        }

CreateLambda()方法根据判断条件的类型,调用Expression的Equal、NotEqual、GreaterThan、LessThan、LessThanOrEqual、GreaterThanOrEqual方法来生成lambda表达式,而对于”包含”和”不包含”,需要调用字符串的Contains()方法来完成。而这部分逻辑在GetExpressionWithMethod()和GetExpressionWithoutMethod()方法中实现。
“不包含”是在可看成是” 包含”的否定(Not)。

        public static Expression<Func<T, bool>> GetExpressionWithMethod<T>(string methodName, FilterModel filterCondition)        {            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");            MethodCallExpression methodExpression = GetMethodExpression(methodName, filterCondition.column, filterCondition.value, parameterExpression);            return Expression.Lambda<Func<T, bool>>(methodExpression, parameterExpression);        }        public static Expression<Func<T, bool>> GetExpressionWithoutMethod<T>(string methodName, FilterModel filterCondition)        {            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");            MethodCallExpression methodExpression = GetMethodExpression(methodName, filterCondition.column, filterCondition.value, parameterExpression);            var notMethodExpression = Expression.Not(methodExpression);            return Expression.Lambda<Func<T, bool>>(notMethodExpression, parameterExpression);        }        /// <summary>        /// 生成类似于p=>p.values.Contains("xxx");的lambda表达式        /// parameterExpression标识p,propertyName表示values,propertyValue表示"xxx",methodName表示Contains        /// 仅处理p的属性类型为string这种情况        /// </summary>        /// <param name="methodName"></param>        /// <param name="propertyName"></param>        /// <param name="propertyValue"></param>        /// <param name="parameterExpression"></param>        /// <returns></returns>        private static MethodCallExpression GetMethodExpression(string methodName, string propertyName, string propertyValue, ParameterExpression parameterExpression)        {                        var propertyExpression = Expression.Property(parameterExpression, propertyName);            MethodInfo method = typeof(string).GetMethod(methodName, new[] { typeof(string) });            var someValue = Expression.Constant(propertyValue, typeof(string));            return Expression.Call(propertyExpression, method, someValue);        }

最后,介绍一下拼接lambda表达式的实现代码。LinqBuilder类提供 Or()和And()来拼接关系是”且”和”或”的lambda表达式。这部分代码来源于网络。

    public static class LinqBuilder    {        /// <summary>        /// 默认True条件        /// </summary>        /// <typeparam name="T"></typeparam>        /// <returns></returns>        public static Expression<Func<T, bool>> True<T>() { return f => true; }        /// <summary>        /// 默认False条件        /// </summary>        /// <typeparam name="T"></typeparam>        /// <returns></returns>        public static Expression<Func<T, bool>> False<T>() { return f => false; }        /// <summary>        /// 拼接 OR 条件        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="exp"></param>        /// <param name="condition"></param>        /// <returns></returns>        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> condition)        {            var inv = Expression.Invoke(condition, exp.Parameters.Cast<Expression>());            return Expression.Lambda<Func<T, bool>>(Expression.Or(exp.Body, inv), exp.Parameters);        }        /// <summary>        /// 拼接And条件        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="exp"></param>        /// <param name="condition"></param>        /// <returns></returns>        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> condition)        {            var inv = Expression.Invoke(condition, exp.Parameters.Cast<Expression>());            return Expression.Lambda<Func<T, bool>>(Expression.And(exp.Body, inv), exp.Parameters);        }    }
0 0