Linq动态查询简易解决之道(原创)_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > Linq动态查询简易解决之道(原创)

Linq动态查询简易解决之道(原创)

 2014/5/18 13:24:11  云在青天水在哪  博客园  我要评论(0)
  • 摘要:因为项目需要使用Linq来查询数据,但是在多条件查询时,需要使用一大堆if(...!=string.empty)等判断条件感觉不是很优雅。网上搜索以下,大概找到了两种办法,一种是老外写的一个类,感觉用着麻烦;还有就是提供一扩展个方法,参数为某个类型,当调用该方法时,用反射去遍历这个类型的属性,再拿动态查询参数和属性值去比较,然后构建动态lambda表达式,这个也有缺陷,就是需要遍历类型的所有属性,而且构建lambda表达式只能构建==类型表达式,有局限性。所以自己琢磨了一个办法
  • 标签:原创 解决

 因为项目需要使用Linq来查询数据,但是在多条件查询时,需要使用一大堆if(...!=string.empty)等判断条件感觉不是很优雅。网上搜索以下,大概找到了两种办法,一种是老外写的一个类,感觉用着麻烦;还有就是提供一扩展个方法,参数为某个类型,当调用该方法时,用反射去遍历这个类型的属性,再拿动态查询参数和属性值去比较,然后构建动态lambda表达式,这个也有缺陷,就是需要遍历类型的所有属性,而且构建lambda表达式只能构建==类型表达式,有局限性。所以自己琢磨了一个办法,调用时只需一行代码,lambda表达式可以随意构建,现在进入主题。
   假设有一个类型Employee,并且该类型有集合employeeList,有这样一个基于IEnumerable<T>类型的扩展方法Wheres(稍后介绍),那怎样用
一行代码employeeList.Wheres(o => o.Name == "a" && o.Salary > 5000 && o.InDate >= DateTime.Now && o.Address.Contains("北京"))去实现如下一堆代码:

    if (!string.IsNullOrEmpty(name))
            {
                employeeList.Where(o => o.Name == name);
            }
            if (salary != null)参数设置为可空
            {
                employeeList.Where(o => o.Name == name);
            }
            if (inDate != null)
            {
                employeeList.Where(o => o.InDate>=inDate);
            }
            if (!string.IsNullOrEmpty(address))
            {
                employeeList.Where(o => o.Address.Contains("北京"));
            }

  因为Linq Lambda表达式在运行的时候会被解析成一棵表达式的hashu.html" target="_blank">二叉树,这棵树只允许左边的子节点存在BinaryExpression子节点,而右边的子节点不存在BinaryExpression子节点,但可以存在MemberExpression子节点(o.Name=="a"就是一个BinaryExpression,o.Name以及"a"就是BinaryExpression)。所以employeeList.Wheres(o => o.Name == "a" && o.Salary > 5000 && o.InDate >= DateTime.Now && o.Address == "北京")将会被解析成如下图的一个课二叉树:

我们只需找出红色节点,然后得到蓝色节点的值,然后去构建动态Lambda表达式就可以了,所以的获取节点的方法如下:

class="code_img_closed" src="/Upload/Images/2014051813/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('0bb78432-2d07-4f09-9ea7-764955f5733f',event)" src="/Upload/Images/2014051813/2B1B950FA3DF188F.gif" alt="" />
 /// <summary>
        /// 分解表达式树
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        private static Stack<Expression> DivideBinaryExpression(Expression expression)
        {
            Stack<Expression> stack = new Stack<Expression>();

            if (expression.NodeType != ExpressionType.AndAlso) //为了简化调用代码,只允许根节点为&&
            {
                stack.Push(expression);
            }
            else
            {
                BinaryExpression tree = expression as BinaryExpression;
                while (tree != null && tree.NodeType == ExpressionType.AndAlso)
                {
                    stack.Push(tree.Right);
                    if (tree.Left.NodeType != ExpressionType.AndAlso)
                    {
                        stack.Push(tree.Left);
                    }
                    tree = tree.Left as BinaryExpression;//循环遍历表达式
                }
            }
            return stack;
        }
View Code

然后再根据得到的节点去动态构建Lambda表达式,方法如下:

/// <summary>
        /// 多where子句查询
        /// </summary>
        /// <typeparam name="TSource">实体类型</typeparam>
        /// <typeparam name="TResult">Expression的返回类型</typeparam>
        /// <param name="t">实体集合</param>
        /// <param name="expression">表达式</param>
        /// <returns>实体集合</returns>
        public static IEnumerable<TSource> Wheres<TSource>(this IEnumerable<TSource> t, Expression<Func<TSource, bool>> expression)
        {
            foreach (Expression e in DivideBinaryExpression(expression.Body))
            {
                object expressionValue = null;
                if ((e as BinaryExpression) != null)
                {
                    BinaryExpression be = e as BinaryExpression;
                    expressionValue = LambdaExpression.Lambda(be.Right).Compile().DynamicInvoke();
                }
                else //为了处理像o.Address.Contains("北京")这样的特殊节点
                {
                    MethodCallExpression mce = e as MethodCallExpression;
                    expressionValue = LambdaExpression.Lambda(mce.Arguments[0]).Compile().DynamicInvoke();
                }
                if (expressionValue != null)
                {
                    if (!string.IsNullOrEmpty(expressionValue.ToString()))
                        t = t.Where(Expression.Lambda<Func<TSource, bool>>(e, expression.Parameters).Compile());
                }
            }
            return t;
        }
View Code

在调用的时候只需像上面提到的一行代码就够了,虽然不是很完善,但至少能解决90%以上的需求.

发表评论
用户名: 匿名