关于Expression表达式树的拼接

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

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

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

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

首先咱们须要知道,表达式构成部分,表达式是有两部分构成,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的表达式.这就是这个问题的 解决思路,来看看咱们是怎么来实现的.io

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

Expression<Func<UserInfo, bool>> where;class

而后咱们开始进行labmda的构造变量

 

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

ParameterExpression param = Expression.Parameter(typeof(UserInfo), "c");//c=>lambda

 

            //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);

        }

}

固然,这个帮助类功能有限,若是有须要者,你们能够本身进行扩充.

 

本文所提到的技术,均为我师研究,由于他研究完以后就给咱们讲解了原理和实现.我只是整理出来,给你们作

相关文章
相关标签/搜索