翼度科技»论坛 编程开发 .net 查看内容

Csharp中表达式树

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
Csharper中的表达式树

这节课来了解一下表示式树是什么?
在C#中,表达式树是一种数据结构,它可以表示一些代码块,如Lambda表达式或查询表达式。表达式树使你能够查看和操作数据,就像你可以查看和操作代码一样。它们通常用于创建动态查询和解析表达式。
一、认识表达式树

为什么要这样说?它和委托有什么区别?
创建一个简单的表达式树和委托
  1.   public class ExpressionDemo
  2.   {
  3.       void Show()
  4.       {
  5.           Func<int, bool> fun1 = x => x > 10;
  6.           Expression<Func<int, bool>> expression1 = x => x > 10;
  7.       }
  8.   }
复制代码
然后f12转到定义
  1. public sealed class Expression<TDelegate> : LambdaExpression
复制代码
尝试用大括号定义一个表达式树

debug运行后,用vs查看一下定义的表达式树对象.

发现表达式树一些特点:

  • 可以通过lambda表达式来声明
  • 是一个泛型类的接口,类型参数是一个委托
  • Expression声明中,不能包含大括号.
  • 通过VS展开查看,包含body(lamubda的主体部分),描述了参数的名称和类型,描述了返回值的名称和类型; 展开body, body包含 左边是什么,右边是什么,式子的操作类型是什么.
结论:
表达式树,是一个计算式的描述,按照常规的计算逻辑,通过类的属性来进行描述多个节点之间的关系; 形似于一个树形结构----二叉树; 二叉树不断地去分解,可以得到这个式子中的任何一个独立的元素;----是一个二叉树,是一个数据结构; 如果需要可以把这个结构不断的拆解;得到中间的最小元素;在需要的时候,也可以通过每个元素,组装起来;
委托是一个类,而表达式树是一个二叉树的数据结构。
为了更加深入的了解表达式树,这里也使用ilspy进行反编译,以便于更加了解表达式树的本质.
这里使用一个比较复杂的表达式树的语句来方便我们去理解
  1. Expression<Func<int ,int ,int>> expression2= (x, y) => x *y+2+3;
复制代码

优化一下这段代码
  1. //定义2个变量
  2. ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
  3. ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "y");
  4. //定义常量
  5. var contact1 = Expression.Constant(2, typeof(int));
  6. var contact2= Expression.Constant(3, typeof(int));
  7. //定义表达式 x*y
  8. var MultiplyXy= Expression.Multiply(parameterExpression, parameterExpression2);
  9. //定义表达式 x*y的结果+2
  10. var add1 = Expression.Add(MultiplyXy, contact1);
  11. //定义表达式 x*y+2的结果+3
  12. var add2 = Expression.Add(add1, contact2);
  13. //定义最终的lambda表达式
  14. Expression<Func<int, int, int>> expression2 = Expression.Lambda<Func<int, int, int>>(add2, new ParameterExpression[2]
  15. {
  16.     parameterExpression,
  17.     parameterExpression2
  18. });
复制代码
如图所示的解析:

已经将相应的代码粘贴到上方,就是类似二叉树结构的因式分解,转换成为最小的子问题,最后解决一个需要解决的大问题。
二、动态拼装Expression

我们自己去拼装一个表达式树去理解表达式树的秘密.
首先创建一个People类
  1. public class People
  2. {
  3.     public int Age { get; set; }
  4.     public string Name { get; set; }
  5.     public int Id;
  6. }
复制代码
下面来拼接一个比较复杂的表达式
  1. Expression<Func<People, bool>> predicate = c => c.Id == 10 && c.Name.ToString().Equals("张三");
复制代码
对应的表达式树的代码
  1.   //定义一个People类型的参数
  2.   ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
  3.   //获取People的Id属性
  4.   PropertyInfo? propertyId = typeof(People).GetProperty("Id");
  5.   //定义10这个常量
  6.   ConstantExpression constantExpression = Expression.Constant(10, typeof(int));               
  7.   //定义c.Id>10这个表达式   
  8.   BinaryExpression left =Expression.GreaterThan(Expression.Property(parameterExpression, propertyId), constantExpression);
  9.   //获取People的Name属性
  10.   PropertyInfo? propertyName = typeof(People).GetProperty("Name");
  11.   //c.Name
  12.   MemberExpression memName = Expression.Property(parameterExpression, propertyName);
  13.   //to string方法
  14.   MethodInfo? methodtostring=typeof(string).GetMethod("ToString",new Type[0]);
  15.   //调用tostring方法
  16.   MethodCallExpression instance =Expression.Call(memName, methodtostring,Array.Empty<Expression>());
  17.   //获取equals方法
  18.   MethodInfo? methodEquals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
  19.   //定义c.Name.ToString().Equals("张三")这个表达式
  20.   MethodCallExpression right = Expression.Call(instance, methodEquals, Expression.Constant("张三", typeof(string)));
  21.   //定义c.Age<25这个表达式
  22.   PropertyInfo? propertyAge = typeof(People).GetProperty("Age");
  23.   ConstantExpression constantExpression2 = Expression.Constant(25, typeof(int));
  24.   BinaryExpression right2 = Expression.LessThan(Expression.Property(parameterExpression, propertyAge), constantExpression2);
  25.   //定义c.Id>10 && c.Name.ToString().Equals("张三") && c.Age<25这个表达式
  26.   BinaryExpression and1 = Expression.AndAlso(left, right);
  27.   BinaryExpression and2 = Expression.AndAlso(and1, right2);
  28.   //定义最终的lambda表达式
  29.   Expression<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(and2, new ParameterExpression[1]
  30.   {
  31.       parameterExpression
  32.   });
  33.   //编译表达式
  34.   Func<People, bool> func = expression.Compile();
  35.   //调用表达式
  36.   People people = new People()
  37.   {
  38.       Id = 11,
  39.       Name = "张三",
  40.       Age = 20
  41.   };
  42.   Console.WriteLine(func(people));
复制代码
拼接后的结果
  1. Expression<Func<People, bool>> expression2 = p => p.Id == 10 && p.Name.Equals("阳光下的微笑");
复制代码
例子2
  1. //按关键字是否存在来拼装;
  2. Expression<Func<People, bool>> exp = p=> true;
  3. Console.WriteLine("用户输入个名称,为空就跳过");
  4. string name = Console.ReadLine();
  5. if (!string.IsNullOrWhiteSpace(name))
  6. {
  7.     //exp = p => p.Name.Contains(name);
  8.     exp= exp.And(c=>c.Name.Contains(name));
  9. }
  10. Console.WriteLine("用户输入个最小年纪,为空就跳过");
  11. string age = Console.ReadLine();
  12. if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge))
  13. {
  14.    //  exp = p => p.Age > iAge;
  15.     exp = exp.And(p => p.Age > iAge);
  16. }
复制代码
现在使用表达式树进行链接
  1. Expression lambda1 = x => x.Age > 5; Expression lambda2 = x => x.Id > 5; //按关键字是否存在来拼装;
  2. Expression<Func<People, bool>> exp = p=> true;
  3. Console.WriteLine("用户输入个名称,为空就跳过");
  4. string name = Console.ReadLine();
  5. if (!string.IsNullOrWhiteSpace(name))
  6. {
  7.     //exp = p => p.Name.Contains(name);
  8.     exp= exp.And(c=>c.Name.Contains(name));
  9. }
  10. Console.WriteLine("用户输入个最小年纪,为空就跳过");
  11. string age = Console.ReadLine();
  12. if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge))
  13. {
  14.    //  exp = p => p.Age > iAge;
  15.     exp = exp.And(p => p.Age > iAge);
  16. }; Expression lambda3 = lambda1.And(lambda2); //且   两个都满足,通过&&链接 Expression lambda4 = lambda1.Or(lambda2);//或   两个只要有一个就可以  通过或者来链接  ||  Expression lambda5 = lambda1.Not();//非
复制代码
这里实现了常见的且、或、非逻辑运算符的表达式
  1. Expression<Func<People, bool>> lambda1 = x => x.Age > 5;
  2. Expression<Func<People, bool>> lambda2 = x => x.Id > 5;
  3. //Expression<Func<People, bool>> newExpress = x => x.Age > 5 && x.Id > 5;
  4. Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2); //且   两个都满足,通过&&链接
  5. Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);//或   两个只要有一个就可以  通过或者来链接  ||
  6. Expression<Func<People, bool>> lambda5 = lambda1.Not();//非
复制代码
现在有一个新的需求,需要把People拷贝到NewPeople这个新的类,来看下效率怎么样?
People和PeopleCopy类
  1. public static class ExpressionExtend
  2. {
  3.      /// <summary>
  4.      /// 合并表达式 expr1 AND expr2
  5.      /// </summary>
  6.      /// <typeparam name="T"></typeparam>
  7.      /// <param name="expr1"></param>
  8.      /// <param name="expr2"></param>
  9.      /// <returns></returns>
  10.      public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
  11.      {
  12.          //return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters);
  13.          ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
  14.          NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
  15.          var left = visitor.Replace(expr1.Body);
  16.          var right = visitor.Replace(expr2.Body); //为了能够生成一个新的表达式目录树
  17.          var body = Expression.And(left, right);
  18.           return Expression.Lambda<Func<T, bool>>(body, newParameter);
  19.      }
  20.      /// <summary>
  21.      /// 合并表达式 expr1 or expr2
  22.      /// </summary>
  23.      /// <typeparam name="T"></typeparam>
  24.      /// <param name="expr1"></param>
  25.      /// <param name="expr2"></param>
  26.      /// <returns></returns>
  27.      public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
  28.      {
  29.          ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
  30.          NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
  31.          var left = visitor.Replace(expr1.Body);
  32.          var right = visitor.Replace(expr2.Body);
  33.          var body = Expression.Or(left, right);
  34.          return Expression.Lambda<Func<T, bool>>(body, newParameter);
  35.      }
  36.      public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
  37.      {
  38.          var candidateExpr = expr.Parameters[0];
  39.          var body = Expression.Not(expr.Body);
  40.          return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
  41.      }
  42. }
  43. internal class NewExpressionVisitor : ExpressionVisitor
  44. {
  45.     public ParameterExpression _NewParameter { get; private set; }
  46.     public NewExpressionVisitor(ParameterExpression param)
  47.     {
  48.         this._NewParameter = param;
  49.     }
  50.     public Expression Replace(Expression exp)
  51.     {
  52.         return this.Visit(exp);
  53.     }
  54.     protected override Expression VisitParameter(ParameterExpression node)
  55.     {
  56.         return this._NewParameter;
  57.     }
  58. }
复制代码
直接赋值的方式
  1. public class People
  2. {
  3.      public int Age { get; set; }
  4.      public string Name { get; set; }
  5.      public int Id;
  6. }
  7. /// <summary>
  8. /// 实体类Target
  9. /// PeopleDTO
  10. /// </summary>
  11. public class PeopleCopy
  12. {
  13.      public int Age { get; set; }
  14.      public string Name { get; set; }
  15.      public int Id;
  16. }
复制代码
反射赋值的方式
  1. PeopleCopy peopleCopy1 = new PeopleCopy()
  2. {
  3.      Id = people.Id,
  4.      Name = people.Name,
  5.      Age = people.Age
  6. };
复制代码
json序列化的方式
  1. public class ReflectionMapper
  2. {
  3.      /// <summary>
  4.      /// 反射
  5.      /// </summary>
  6.      /// <typeparam name="TIn"></typeparam>
  7.      /// <typeparam name="TOut"></typeparam>
  8.      /// <param name="tIn"></param>
  9.      /// <returns></returns>
  10.      public static TOut Trans<TIn, TOut>(TIn tIn)
  11.      {
  12.          TOut tOut = Activator.CreateInstance<TOut>();
  13.          foreach (var itemOut in tOut.GetType().GetProperties())
  14.          {
  15.              var propName = tIn.GetType().GetProperty(itemOut.Name);
  16.              itemOut.SetValue(tOut, propName.GetValue(tIn));
  17.          }
  18.          foreach (var itemOut in tOut.GetType().GetFields())
  19.          {
  20.              var fieldName = tIn.GetType().GetField(itemOut.Name);
  21.              itemOut.SetValue(tOut, fieldName.GetValue(tIn));
  22.          }
  23.          return tOut;
  24.      }
  25. }
  26. PeopleCopy peopleCopy2= ReflectionMapper.Trans<People, PeopleCopy>(people);
复制代码
表达式目录树的方式
  1. public class SerializeMapper
  2. {
  3.     /// <summary>
  4.     /// 序列化反序列化方式
  5.     /// </summary>
  6.     /// <typeparam name="TIn"></typeparam>
  7.     /// <typeparam name="TOut"></typeparam>
  8.     public static TOut Trans<TIn, TOut>(TIn tIn)
  9.     {
  10.         string strTin = JsonConvert.SerializeObject(tIn);
  11.         return JsonConvert.DeserializeObject<TOut>(strTin);
  12.     }
  13. }
  14. PeopleCopy peopleCopy3 = SerializeMapper.Trans<People, PeopleCopy>(people);
复制代码
表达式+反射+泛型类的方式
  1. public class ExpressionMapper
  2. {
  3.     /// <summary>
  4.     /// 字典缓存--hash分布
  5.     /// </summary>
  6.     private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
  7.     /// <summary>
  8.     /// 字典缓存表达式树
  9.     /// </summary>
  10.     /// <typeparam name="TIn"></typeparam>
  11.     /// <typeparam name="TOut"></typeparam>
  12.     /// <param name="tIn"></param>
  13.     /// <returns></returns>
  14.     public static TOut Trans<TIn, TOut>(TIn tIn)
  15.     {
  16.         string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
  17.         if (!_Dic.ContainsKey(key))
  18.         {
  19.             #region 这里是拼装---赋属性值的代码
  20.             ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
  21.             //MemberBinding: 就是一个表达式目录树
  22.             List<MemberBinding> memberBindingList = new List<MemberBinding>();
  23.             foreach (var item in typeof(TOut).GetProperties())   //这里是处理属性的
  24.             {
  25.                 MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
  26.                 MemberBinding memberBinding = Expression.Bind(item, property);
  27.                 memberBindingList.Add(memberBinding);
  28.             }
  29.             foreach (var item in typeof(TOut).GetFields()) //处理字段的
  30.             {
  31.                 MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
  32.                 MemberBinding memberBinding = Expression.Bind(item, property);
  33.                 memberBindingList.Add(memberBinding);
  34.             }
  35.             MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());  //组装了一个转换的过程;
  36.             Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
  37.             {
  38.                 parameterExpression
  39.             });
  40.             #endregion
  41.             Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
  42.             _Dic[key] = func;
  43.         }
  44.         return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
  45.     }
  46. }
  47. PeopleCopy peopleCopy4 = ExpressionMapper.Trans<People, PeopleCopy>(people);
复制代码
最后运行一百万次,来看一下效率。
  1.     public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
  2.     {
  3.         private static Func<TIn, TOut> _FUNC = null;
  4.         static ExpressionGenericMapper()
  5.         {
  6.             ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
  7.             List<MemberBinding> memberBindingList = new List<MemberBinding>();
  8.             foreach (var item in typeof(TOut).GetProperties())
  9.             {
  10.                 MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
  11.                 MemberBinding memberBinding = Expression.Bind(item, property);
  12.                 memberBindingList.Add(memberBinding);
  13.             }
  14.             foreach (var item in typeof(TOut).GetFields())
  15.             {
  16.                 MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
  17.                 MemberBinding memberBinding = Expression.Bind(item, property);
  18.                 memberBindingList.Add(memberBinding);
  19.             }
  20.             MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
  21.             Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
  22.             {
  23.                     parameterExpression
  24.             });
  25.             _FUNC = lambda.Compile();//拼装是一次性的
  26.         }
  27.         public static TOut Trans(TIn t)
  28.         {
  29.             return _FUNC(t);
  30.         }
  31.     }
  32. }
  33. PeopleCopy peopleCopy5 = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
复制代码
看运行后的结果

核心:动态生成硬编码;----代码运行的时候生成了一段新的逻辑;
四、表达式树和sql

为什么要使用表达式目录树来拼装解析呢?
可以提供重用性
如果封装好一个方法,接受一个表达式树,在解析的时候,其实就是不断的访问,访问的时候,会按照固定的规则,避免出错;
任何的一个表达式树都可以用一个通用的方法解析并且支持泛型,更加容易去封装;
例子:
需要的扩展类
  1. {
  2.     People people = new People()
  3.     {
  4.         Id = 11,
  5.         Name = "Richard",
  6.         Age = 31
  7.     };
  8.     long common = 0;
  9.     long generic = 0;
  10.     long cache = 0;
  11.     long reflection = 0;
  12.     long serialize = 0;
  13.     {
  14.         Stopwatch watch = new Stopwatch();
  15.         watch.Start();
  16.         for (int i = 0; i < 1_000_000; i++)
  17.         {
  18.             PeopleCopy peopleCopy = new PeopleCopy()
  19.             {
  20.                 Id = people.Id,
  21.                 Name = people.Name,
  22.                 Age = people.Age
  23.             };
  24.         }
  25.         watch.Stop();
  26.         common = watch.ElapsedMilliseconds;
  27.     }
  28.     {
  29.         Stopwatch watch = new Stopwatch();
  30.         watch.Start();
  31.         for (int i = 0; i < 1_000_000; i++)
  32.         {
  33.             PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people);
  34.         }
  35.         watch.Stop();
  36.         reflection = watch.ElapsedMilliseconds;
  37.     }
  38.     {
  39.         Stopwatch watch = new Stopwatch();
  40.         watch.Start();
  41.         for (int i = 0; i < 1_000_000; i++)
  42.         {
  43.             PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people);
  44.         }
  45.         watch.Stop();
  46.         serialize = watch.ElapsedMilliseconds;
  47.     }
  48.     {
  49.         Stopwatch watch = new Stopwatch();
  50.         watch.Start();
  51.         for (int i = 0; i < 1_000_000; i++)
  52.         {
  53.             PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people);
  54.         }
  55.         watch.Stop();
  56.         cache = watch.ElapsedMilliseconds;
  57.     }
  58.     {
  59.         Stopwatch watch = new Stopwatch();
  60.         watch.Start();
  61.         for (int i = 0; i < 1_000_000; i++)
  62.         {
  63.             PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
  64.         }
  65.         watch.Stop();
  66.         generic = watch.ElapsedMilliseconds;
  67.     }
  68.     Console.WriteLine($"common = {common} ms");        //性能最高,但是不能通用;
  69.     Console.WriteLine($"reflection = {reflection} ms");
  70.     Console.WriteLine($"serialize = {serialize} ms");
  71.     Console.WriteLine($"cache = {cache} ms");
  72.     Console.WriteLine($"generic = {generic} ms"); //性能好,而且扩展性也好===又要马儿跑,又要马儿不吃草。。。
  73. }
复制代码
对应的表达式解析
  1. public class OperationsVisitor : ExpressionVisitor
  2. {
  3.      public Expression Modify(Expression expression)
  4.      {
  5.          Console.WriteLine(expression.ToString()) ;
  6.          //ExpressionVisitor:
  7.          //1.Visit方法--访问表达式目录树的入口---分辨是什么类型的表达式目录
  8.          //2.调度到更加专业的方法中进一步访问,访问一遍之后,生成一个新的表达式目录   ---有点像递归,不全是递归;
  9.          //3.因为表达式目录树是个二叉树,ExpressionVisitor一直往下访问,一直到叶节点;那就访问了所有的节点
  10.          //4.在访问的任何一个环节,都可以拿到对应当前环节的内容(参数名称、参数值。。),就可以进一步扩展
  11.          return this.Visit(expression);
  12.      }
  13.      /// <summary>
  14.      /// 覆写父类方法
  15.      /// </summary>
  16.      /// <param name="b"></param>
  17.      /// <returns></returns>
  18.      protected override Expression VisitBinary(BinaryExpression b)
  19.      {
  20.         
  21.          if (b.NodeType == ExpressionType.Add)
  22.          {
  23.              Expression left = this.Visit(b.Left);
  24.              Expression right = this.Visit(b.Right);
  25.              return Expression.Subtract(left, right);
  26.          }
  27.          else if (b.NodeType==ExpressionType.Multiply) //如果是相乘
  28.          {
  29.              Expression left = this.Visit(b.Left);
  30.              Expression right = this.Visit(b.Right);
  31.              return Expression.Divide(left, right); //相除
  32.          }
  33.          return base.VisitBinary(b);
  34.      }
  35.      /// <summary>
  36.      /// 覆写父类方法
  37.      /// </summary>
  38.      /// <param name="node"></param>
  39.      /// <returns></returns>
  40.      protected override Expression VisitConstant(ConstantExpression node)
  41.      {
  42.          return base.VisitConstant(node);
  43.      }
复制代码
同时表达式树中已经通过使用观察者模式封装好了Visit方法.

  • Visit方法--访问表达式树的入口---分辨是什么类型的表达式目录
  • 调度到更加专业的方法中进一步访问,访问一边以后,生成一个新的表达式目录. --- 有点像递归,不全是递归
  • 因为表达式目录树是一个二叉树,ExpreesionVistor一直往下访问,一直到叶子节点;通过二叉树的遍历就访问了所有的节点.
  • 在访问的任何一个环节,都可以拿到对应当前环节的内容(参数名称、参数值...)就可以进一步扩展.
现在开始将表达式树跟sql语句进行连接
例子:
扩展类
  1. Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
  2. OperationsVisitor visitor = new OperationsVisitor();
  3. //visitor.Visit(exp);
  4. Expression expNew = visitor.Modify(exp);
复制代码
对应的sql语句的解析
  1. public class ConditionBuilderVisitor : ExpressionVisitor
  2. {
  3.      private Stack<string> _StringStack = new Stack<string>();
  4.      public string Condition()
  5.      {
  6.          string condition = string.Concat(this._StringStack.ToArray());
  7.          this._StringStack.Clear();
  8.          return condition;
  9.      }
  10.      /// <summary>
  11.      /// 如果是二元表达式
  12.      /// </summary>
  13.      /// <param name="node"></param>
  14.      /// <returns></returns>
  15.      protected override Expression VisitBinary(BinaryExpression node)
  16.      {
  17.          if (node == null) throw new ArgumentNullException("BinaryExpression");
  18.          this._StringStack.Push(")");
  19.          base.Visit(node.Right);//解析右边
  20.          this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
  21.          base.Visit(node.Left);//解析左边
  22.          this._StringStack.Push("(");
  23.          return node;
  24.      }
  25.      /// <summary>
  26.      /// 解析属性
  27.      /// </summary>
  28.      /// <param name="node"></param>
  29.      /// <returns></returns>
  30.      protected override Expression VisitMember(MemberExpression node)
  31.      {
  32.          if (node == null) throw new ArgumentNullException("MemberExpression");
  33.          //this._StringStack.Push(" [" + node.Member.Name + "] ");
  34.          ////return node;
  35.          if (node.Expression is ConstantExpression)
  36.          {
  37.              var value1 = this.InvokeValue(node);
  38.              var value2 = this.ReflectionValue(node);
  39.              //this.ConditionStack.Push($"'{value1}'");
  40.              this._StringStack.Push("'" + value2 + "'");
  41.          }
  42.          else
  43.          {
  44.              this._StringStack.Push(" [" + node.Member.Name + "] ");
  45.          }
  46.          return node;
  47.      }
  48.      private object InvokeValue(MemberExpression member)
  49.      {
  50.          var objExp = Expression.Convert(member, typeof(object));//struct需要
  51.          return Expression.Lambda<Func<object>>(objExp).Compile().Invoke();
  52.      }
  53.      private object ReflectionValue(MemberExpression member)
  54.      {
  55.          var obj = (member.Expression as ConstantExpression).Value;
  56.          return (member.Member as FieldInfo).GetValue(obj);
  57.      }
  58.      /// <summary>
  59.      /// 常量表达式
  60.      /// </summary>
  61.      /// <param name="node"></param>
  62.      /// <returns></returns>
  63.      protected override Expression VisitConstant(ConstantExpression node)
  64.      {
  65.          if (node == null) throw new ArgumentNullException("ConstantExpression");
  66.          this._StringStack.Push(" '" + node.Value + "' ");
  67.          return node;
  68.      }
  69.      /// <summary>
  70.      /// 方法表达式
  71.      /// </summary>
  72.      /// <param name="m"></param>
  73.      /// <returns></returns>
  74.      protected override Expression VisitMethodCall(MethodCallExpression m)
  75.      {
  76.          if (m == null) throw new ArgumentNullException("MethodCallExpression");
  77.          string format;
  78.          switch (m.Method.Name)
  79.          {
  80.              case "StartsWith":
  81.                  format = "({0} LIKE {1}+'%')";
  82.                  break;
  83.              case "Contains":
  84.                  format = "({0} LIKE '%'+{1}+'%')";
  85.                  break;
  86.              case "EndsWith":
  87.                  format = "({0} LIKE '%'+{1})";
  88.                  break;
  89.              default:
  90.                  throw new NotSupportedException(m.NodeType + " is not supported!");
  91.          }
  92.          this.Visit(m.Object);
  93.          this.Visit(m.Arguments[0]);
  94.          string right = this._StringStack.Pop();
  95.          string left = this._StringStack.Pop();
  96.          this._StringStack.Push(String.Format(format, left, right));
  97.          return m;
  98.      }
  99. }
复制代码
在我自己的看法,使用表达式树而不是传统的方式去解析sql语句的优点

  • 通过二叉树的方式表达,更加的有条理性
  • 使用泛型等技术更方式实现一个通用的sql语句的解析。
  • 会有类型检查,出错后也能使用异常处理。

来源:https://www.cnblogs.com/wenlong-4613615/p/18084777
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具