zl程序教程

您现在的位置是:首页 >  其它

当前栏目

关于Expression表达式树的拼接

关于 表达式 拼接 expression
2023-09-11 14:14:39 时间

关于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

 

 

 

 

怎样使用表达式树生成动态查询

2018-01-11 12:11 by 沉睡的木木夕, 33 阅读, 0 评论, 收藏编辑

[翻译]怎样使用表达式树生成动态查询

在LINQ,表达式树常用于结构化查询,目标资源数据实现了 IQueryable. 例如,LINQ为关系型数据存储查询提供了 IQueryable 接口。C#编译器将这些数据源的查询编译成运行时的表达式树代码。然后查询提供程序可以遍历表达式树数据结构,并转化为合适于数据源的查询语言。

在LINQ中使用表达式树来表示分配给 Expression 类型的Lambda表达式变量。

这节主要描述了如何使用表达式树构建一个动态LINQ查询。在编译期,动态查询在特殊未知的查询的情况下是非常有用的。具体例子,一个应用程序提供了一个用户接口,最终来允许用户指定一个或多个谓词来过滤数据。为了使用LINQ查询,这种情况应用程序在运行时必须使用表达式树来构建一个LINQ查询。

Example

下面这段代码展示如何使用表达式树去围绕 IQueryable 数据源构造一个查询并运行。代码生成了一个表达式树来表示查询:

companies.Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).OrderBy(company => company)

在命名空间 [System.Linq.Expressions](https://docs.microsoft.com/en-us/dotnet/api/system.linq.expressions) 下有个工厂方法用来生成一个表达式树来表示这个查询。表示标准查询运算符方法调用的表达式将引用这些方法的 Queryable 的实现。最终表达式树被传递给 IQueryable 数据源的提供程序的 CreateQuery(Expression) 实现,以创建一个可执行的 IQueryable 类型的查询。通过枚举该查询获得结果。

Expression<Func<string, bool>> expr = name => name.Length > 10 && name.StartsWith("G");
Console.WriteLine(expr);

AndAlsoModifier treeModifier = new AndAlsoModifier();
Expression modifierExpr = treeModifier.Modify(expr);

Console.WriteLine(modifierExpr);

string[] companies = {"Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
        "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
        "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
        "Blue Yonder Airlines", "Trey Research", "The Phone Company",
        "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };
//转化IQueryable数据源
IQueryable<string> queryableData = companies.AsQueryable();
//编写表示谓词参数的表达式树
ParameterExpression pe = Expression.Parameter(typeof(string), "company");
//新建一个表达式树来表示 'company.ToLower() == "coho winery"' 的表达式
Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", Type.EmptyTypes));
Expression right = Expression.Constant("coho winery", typeof(string));
Expression e1 = Expression.Equal(left, right);
//新建一个表达式树来表示 'company.Length > 16' 表达式
left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16,typeof(int));
Expression e2 = Expression.GreaterThan(left, right);
//编译表达式树来生成一个表示'(company.ToLower() == "coho winery" || company.Length > 16)' 的表达式
Expression predicateBody = Expression.OrElse(e1, e2);
//新建一个表达式树来表示 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
MethodCallExpression whereCallExpresstion = Expression.Call(
    typeof(Queryable),
    "Where",
    new Type[] { queryableData.ElementType },
    queryableData.Expression,
    Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));

//排序 OrderBy(company => company)
//新建一个表达式树来表示 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderCallExpresstion = Expression.Call(
    typeof(Queryable),
    "OrderBy",
    new Type[] { queryableData.ElementType, queryableData.ElementType },
    whereCallExpresstion,
    Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));

//新建一个可执行的查询表达式树
IQueryable<string> result = queryableData.Provider.CreateQuery<string>(orderCallExpresstion);

//枚举结果
foreach (string company in companies)
    Console.WriteLine(company);

代码中在被传递到 Queryable.Where 方法中,在谓词中使用了一个固定数字。但是,你可以写一个应用程序,来编译在谓词中一个依赖于用户输入的数字变量。你也可以根据用户的输入,更改查询中调用的标准查询操作符。

编译代码

  • 创建新的控制台应用程序项目。
  • 添加对 System.Core.dll 的引用(如果尚未引用)。
  • 包括 System.Linq.Expressions 命名空间。
  • 从示例中复制代码,并将其粘贴到 Main 方法中。
希望有个生活精彩的程序人生