zoukankan      html  css  js  c++  java
  • 关于Expression表达式树的拼接

    最近在做项目中遇到一个问题,需求是这样的:

    我要对已经存在的用户进行检索,可以根据用户的id 或者用户名其中的一部分字符来检索出来,这样就出现了三种情况 只有id,只有用户名中一部字符,或者全部都有.

    我们用的MVC+EF5.0的框架,在BLL层进行查询的 时候需要构建lambda表达式来作为查询条件,但是,我们怎么来构建lambda来确定查询的条件呢?我们知道Express<Func<T,bool>>这样的一个参数可以是lambda表达式,但是这里的按条件拼接式不能使用委托链的形式的.当然还有一种解决办法,我把所有查询条件都写好,然后根据传过来的ID 或者用户名 来判断确定使用哪个..这样的判断逻辑混乱,代码冗长,我们就想找一个可以动态拼接查询条件的方法.

    即按照id 或者用户名是否存在动态的来拼接查询条件.

    首先我们需要知道,表达式构成部分,表达式是有两部分构成,Parameter和body,第一个是参数,第二个是表达式体,表达式体是二进制的位运算,也就是 比如(left&right)而left和right要返回的值必须是基本类型的值,也就是可以参与位运算的值.例如(a,b)=>()这个lambda表达式中,ab是参数,括号后面中是表达式体这里面返回的值只能是基本类型.我们要构建一个表达式树,主要就是构建这个表达式体,那么这个表达式体是一个什么样的类型呢 ?BinaryExpression类型,我们只需要构造这个类型,然后通过Expression.And(left,right)或者Expression.Or()这两个方法来构造即可. 这个两个方法返回值就是BinaryExpression的类型对象.然后我们在用Expression.Lambda<Func<T,bool>>(BinaryExpression,Parameter)这个方法将这个表达式树转化为lambda的表达式.这就是这个问题的 解决思路,来看看我们是怎么来实现的.

    首先我们定义了一个表达式变量.

    Expression<Func<UserInfo, bool>> where;

    然后我们开始进行labmda的构造

    接下来,我们来构造参数和必要条件,也是就lambda中的c=>()中的c

    ParameterExpression param = Expression.Parameter(typeof(UserInfo), "c");//c=>
    
                //c=>c.IsDelete==false这里需要不被删除的条件
    
                MemberExpression left1 = Expression.Property(param, typeof(UserInfo).GetProperty("IsDelete"));构建c.IsDelete
    
                ConstantExpression right1 = Expression.Constant(false);//构建一个常量 false
    
                BinaryExpression be = Expression.Equal(left1, right1);构建//c=>c.IsDelete==false 就是现在这个be了

    下面 我们需要根据我们的条件 也就是id和用户名字符串来继续拼接这个表达式

    首先我们来拼接c.UserId==sid

    if (!string.IsNullOrEmpty(Request["sid"]))
    
                {
    
                    //c.UserId==sid
    
                    int sid = int.Parse(Request["sid"]);
    
                    //根据参数的属性构造左表达式c.UserId
    
                    MemberExpression left2 = Expression.Property(param, typeof(UserInfo).GetProperty("UserId"));
    
                    //构造右表达式sid
    
                    ConstantExpression right2 = Expression.Constant(sid);
    
                    //进行合并:cUserId==sid
    
                    BinaryExpression where2 = Expression.Equal(left2, right2);
    
                    //将这个条件与上一个条件进行与合并:c.IsDelete==false && c.UserId==sid
    
                    be = Expression.And(be, where2);
    
                }

    现在我们来拼接第二个条件

    前面我们已经说过,表达式体需要返回的是可以做二进制运算的类型,但是这是个值类型字符串,该怎么办呢?

    在参考了MSDN中的Expression方法中,发现有这样的一个方法.Expression.Call().

    然后看了示例这个

     

    究竟是用来干嘛的??

    我们可以用这个call’方法 ,来调用一个类型 中的一个方法,然后产生一个MethodCallExpression类型的返回值,这样,我们来调用string. Contains方法不就可以完成我们想要的表达式了么?

    且看下面的 代码

    //c.UserName.Contains(sname)
    
                if (!string.IsNullOrEmpty(Request["sname"]))
    
                {
    
                    string sname = Request["sname"];
    
                    MemberExpression left3 = Expression.Property(param, typeof(UserInfo).GetProperty("UserName"));//这里构造c.UserName这个属性表达式.
    
                    ConstantExpression right3 = Expression.Constant(sname);//这里构造sname这个常量表达式
    
                    MethodCallExpression where3 = Expression.Call(left3, typeof(string).GetMethod("Contains"), right3);这里我们用Call这个方法完成/c.UserName.Contains(sname)这个lambda这个表达式的实现.
    
                    be = Expression.And(be, where3);//拼接刚才的be表达式,
    
                }
    
    where = Expression.Lambda<Func<UserInfo, bool>>(be, param);//生成最后需要的带参数的表达式树.

    这样我们的表达式树拼接就完成了.

    至于运行结果就不为大家贴图了,可以运行和lambda的结果一样.可以完成两个条件的查询.

    下面,封装了这个表达式树的帮助类.大家可以参考.

    public class WhereHelper<T>
    
            where T:class
    
        {
    
            private ParameterExpression param;
    
            private BinaryExpression filter;
    
            public WhereHelper()
    
            {
    
                param = Expression.Parameter(typeof (T), "c");
    
                //1==1
    
                Expression left = Expression.Constant(1);
    
                filter = Expression.Equal(left, left);
    
            }
    
            public Expression<Func<T, bool>> GetExpression()
    
            {
    
                return Expression.Lambda<Func<T, bool>>(filter,param);
    
            }
    
            public void Equal(string propertyName,object value)
    
            {
    
                Expression left = Expression.Property(param, typeof (T).GetProperty(propertyName));
    
                Expression right = Expression.Constant(value, value.GetType());
    
                Expression result = Expression.Equal(left, right);
    
                filter = Expression.And(filter, result);
    
            }
    
            public void Contains(string propertyName,string value)
    
            {
    
                Expression left = Expression.Property(param, typeof (T).GetProperty(propertyName));
    
                Expression right = Expression.Constant(value, value.GetType());
    
                Expression result = Expression.Call(left, typeof (string).GetMethod("Contains"), right);
    
                filter = Expression.And(filter, result);
    
            }
    
    }

    当然,这个帮助类功能有限,如果有需要者,大家可以自己进行扩充.

    本文所提到的技术,均为我师研究,因为他研究完之后就给我们讲解了原理和实现.我只是整理出来,给大家做

    原文  http://www.cnblogs.com/ruhuaxiao/p/3773596.html

  • 相关阅读:
    html 上传图片前预览
    php获取当月天数及当月第一天及最后一天、上月第一天及最后一天实现方法
    php 计算 pdf文件页数
    php 获取半年内每个月的订单数量, 总价, 月份
    php 获取两个数组之间不同的值
    小程序支付功能
    关于nginx的Job for nginx.service failed because the control process exited with error code.错误
    linux 安装 Apollo
    MongoDB待续。。。
    ABP vNext...待续
  • 原文地址:https://www.cnblogs.com/bubugao/p/lambda.html
Copyright © 2011-2022 走看看