Csharp中表達式樹

来源:https://www.cnblogs.com/wenlong-4613615/p/18084777
-Advertisement-
Play Games

Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...


Csharper中的表達式樹

這節課來瞭解一下表示式樹是什麼?

在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。

一、認識表達式樹

為什麼要這樣說?它和委托有什麼區別?

創建一個簡單的表達式樹和委托

  public class ExpressionDemo
  {

      void Show()
      {
          Func<int, bool> fun1 = x => x > 10;
          Expression<Func<int, bool>> expression1 = x => x > 10;
      }
  }

然後f12轉到定義

public sealed class Expression<TDelegate> : LambdaExpression

嘗試用大括弧定義一個表達式樹

img

debug運行後,用vs查看一下定義的表達式樹對象.
img

發現表達式樹一些特點:

  1. 可以通過lambda表達式來聲明
  2. 是一個泛型類的介面,類型參數是一個委托
  3. Expression聲明中,不能包含大括弧.
  4. 通過VS展開查看,包含body(lamubda的主體部分),描述了參數的名稱和類型,描述了返回值的名稱和類型; 展開body, body包含 左邊是什麼,右邊是什麼,式子的操作類型是什麼.

結論:

表達式樹,是一個計算式的描述,按照常規的計算邏輯,通過類的屬性來進行描述多個節點之間的關係; 形似於一個樹形結構----二叉樹; 二叉樹不斷地去分解,可以得到這個式子中的任何一個獨立的元素;----是一個二叉樹,是一個數據結構; 如果需要可以把這個結構不斷的拆解;得到中間的最小元素;在需要的時候,也可以通過每個元素,組裝起來;

委托是一個類,而表達式樹是一個二叉樹的數據結構。

為了更加深入的瞭解表達式樹,這裡也使用ilspy進行反編譯,以便於更加瞭解表達式樹的本質.

這裡使用一個比較複雜的表達式樹的語句來方便我們去理解

Expression<Func<int ,int ,int>> expression2= (x, y) => x *y+2+3;

img

優化一下這段代碼

//定義2個變數
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "y");
//定義常量
var contact1 = Expression.Constant(2, typeof(int));
var contact2= Expression.Constant(3, typeof(int));
//定義表達式 x*y
var MultiplyXy= Expression.Multiply(parameterExpression, parameterExpression2);
//定義表達式 x*y的結果+2
var add1 = Expression.Add(MultiplyXy, contact1);

//定義表達式 x*y+2的結果+3
var add2 = Expression.Add(add1, contact2);
//定義最終的lambda表達式
Expression<Func<int, int, int>> expression2 = Expression.Lambda<Func<int, int, int>>(add2, new ParameterExpression[2]
{
    parameterExpression,
    parameterExpression2
});

如圖所示的解析:

img

已經將相應的代碼粘貼到上方,就是類似二叉樹結構的因式分解,轉換成為最小的子問題,最後解決一個需要解決的大問題。

二、動態拼裝Expression

我們自己去拼裝一個表達式樹去理解表達式樹的秘密.

首先創建一個People類

public class People
{
    public int Age { get; set; }
    public string Name { get; set; }
    public int Id;
}

下麵來拼接一個比較複雜的表達式

Expression<Func<People, bool>> predicate = c => c.Id == 10 && c.Name.ToString().Equals("張三");

對應的表達式樹的代碼

  //定義一個People類型的參數
  ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
  //獲取People的Id屬性
  PropertyInfo? propertyId = typeof(People).GetProperty("Id");
  //定義10這個常量
  ConstantExpression constantExpression = Expression.Constant(10, typeof(int));                
  //定義c.Id>10這個表達式    
  BinaryExpression left =Expression.GreaterThan(Expression.Property(parameterExpression, propertyId), constantExpression);
  //獲取People的Name屬性
  PropertyInfo? propertyName = typeof(People).GetProperty("Name");
  //c.Name
  MemberExpression memName = Expression.Property(parameterExpression, propertyName);
  //to string方法
  MethodInfo? methodtostring=typeof(string).GetMethod("ToString",new Type[0]);
  //調用tostring方法
  MethodCallExpression instance =Expression.Call(memName, methodtostring,Array.Empty<Expression>());
  //獲取equals方法
  MethodInfo? methodEquals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
  //定義c.Name.ToString().Equals("張三")這個表達式
  MethodCallExpression right = Expression.Call(instance, methodEquals, Expression.Constant("張三", typeof(string)));
  //定義c.Age<25這個表達式
  PropertyInfo? propertyAge = typeof(People).GetProperty("Age");
  ConstantExpression constantExpression2 = Expression.Constant(25, typeof(int));
  BinaryExpression right2 = Expression.LessThan(Expression.Property(parameterExpression, propertyAge), constantExpression2);
  //定義c.Id>10 && c.Name.ToString().Equals("張三") && c.Age<25這個表達式
  BinaryExpression and1 = Expression.AndAlso(left, right);
  BinaryExpression and2 = Expression.AndAlso(and1, right2);
  //定義最終的lambda表達式
  Expression<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(and2, new ParameterExpression[1]
  {
      parameterExpression
  });
  //編譯表達式
  Func<People, bool> func = expression.Compile();
  //調用表達式
  People people = new People()
  {
      Id = 11,
      Name = "張三",
      Age = 20
  };
  Console.WriteLine(func(people));

這樣就拼接出來了需要的表達式樹.

三、表達式樹的應用價值

為什麼要拼裝這個表達式目錄樹呢?

現在主流的是Linq:
Linq to Sql -----把相同的邏輯封裝,把不同的邏輯通過表達式目錄樹來傳遞;
傳遞表達式目錄樹:對應的是查詢條件;在傳遞之前就應該把查詢的條件拼裝好;

例子

 Expression<Func<People, bool>> expression2 = p => p.Id == 10 && p.Name.Equals("陽光下的微笑");

拼接後的結果

//按關鍵字是否存在來拼裝;
Expression<Func<People, bool>> exp = p=> true;
Console.WriteLine("用戶輸入個名稱,為空就跳過");
string name = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(name))
{
    //exp = p => p.Name.Contains(name);
    exp= exp.And(c=>c.Name.Contains(name));
}
Console.WriteLine("用戶輸入個最小年紀,為空就跳過");
string age = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge))
{
   //  exp = p => p.Age > iAge;
    exp = exp.And(p => p.Age > iAge);
}

例子2

//Expression<Func<People, bool>> newExpress = x => x.Age > 5 && x.Id > 5

現在使用表達式樹進行鏈接

 Expression<Func<People, bool>> lambda1 = x => x.Age > 5;
 Expression<Func<People, bool>> lambda2 = x => x.Id > 5;

 //Expression<Func<People, bool>> newExpress = x => x.Age > 5 && x.Id > 5;

 Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2); //且   兩個都滿足,通過&&鏈接

 Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);//或   兩個只要有一個就可以  通過或者來鏈接  || 
 Expression<Func<People, bool>> lambda5 = lambda1.Not();//非

這裡實現了常見的且、或、非邏輯運算符的表達式

 public static class ExpressionExtend
 {
     /// <summary>
     /// 合併表達式 expr1 AND expr2
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="expr1"></param>
     /// <param name="expr2"></param>
     /// <returns></returns>
     public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
     {
         //return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters);

         ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
         NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
         var left = visitor.Replace(expr1.Body);
         var right = visitor.Replace(expr2.Body); //為了能夠生成一個新的表達式目錄樹

         var body = Expression.And(left, right);
          return Expression.Lambda<Func<T, bool>>(body, newParameter);

     }
     /// <summary>
     /// 合併表達式 expr1 or expr2
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="expr1"></param>
     /// <param name="expr2"></param>
     /// <returns></returns>
     public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
     {

         ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
         NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);

         var left = visitor.Replace(expr1.Body);
         var right = visitor.Replace(expr2.Body);
         var body = Expression.Or(left, right);
         return Expression.Lambda<Func<T, bool>>(body, newParameter);
     }
     public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
     {
         var candidateExpr = expr.Parameters[0];
         var body = Expression.Not(expr.Body);

         return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
     }
 }

 internal class NewExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression _NewParameter { get; private set; }
    public NewExpressionVisitor(ParameterExpression param)
    {
        this._NewParameter = param;
    }
    public Expression Replace(Expression exp)
    {
        return this.Visit(exp);
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return this._NewParameter;
    }
}

現在有一個新的需求,需要把People拷貝到NewPeople這個新的類,來看下效率怎麼樣?

People和PeopleCopy類

 public class People
 {
     public int Age { get; set; }
     public string Name { get; set; }
     public int Id;
 }
 /// <summary>
 /// 實體類Target
 /// PeopleDTO
 /// </summary>
 public class PeopleCopy
 {

     public int Age { get; set; }
     public string Name { get; set; }
     public int Id;
 }

直接賦值的方式

 PeopleCopy peopleCopy1 = new PeopleCopy()
 {
     Id = people.Id,
     Name = people.Name,
     Age = people.Age
 };

反射賦值的方式

 public class ReflectionMapper
 {
     /// <summary>
     /// 反射
     /// </summary>
     /// <typeparam name="TIn"></typeparam>
     /// <typeparam name="TOut"></typeparam>
     /// <param name="tIn"></param>
     /// <returns></returns>
     public static TOut Trans<TIn, TOut>(TIn tIn)
     {
         TOut tOut = Activator.CreateInstance<TOut>();
         foreach (var itemOut in tOut.GetType().GetProperties())
         {
             var propName = tIn.GetType().GetProperty(itemOut.Name);
             itemOut.SetValue(tOut, propName.GetValue(tIn)); 
         }

         foreach (var itemOut in tOut.GetType().GetFields())
         {
             var fieldName = tIn.GetType().GetField(itemOut.Name);
             itemOut.SetValue(tOut, fieldName.GetValue(tIn)); 
         }
         return tOut;
     }
 }
 PeopleCopy peopleCopy2= ReflectionMapper.Trans<People, PeopleCopy>(people);

json序列化的方式

public class SerializeMapper
{
    /// <summary>
    /// 序列化反序列化方式
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    public static TOut Trans<TIn, TOut>(TIn tIn)
    { 
        string strTin = JsonConvert.SerializeObject(tIn); 
        return JsonConvert.DeserializeObject<TOut>(strTin);
    }
}
 PeopleCopy peopleCopy3 = SerializeMapper.Trans<People, PeopleCopy>(people);

表達式目錄樹的方式


public class ExpressionMapper
{
    /// <summary>
    /// 字典緩存--hash分佈
    /// </summary>
    private static Dictionary<string, object> _Dic = new Dictionary<string, object>();

    /// <summary>
    /// 字典緩存表達式樹
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    /// <param name="tIn"></param>
    /// <returns></returns>
    public static TOut Trans<TIn, TOut>(TIn tIn)
    {
        string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
        if (!_Dic.ContainsKey(key))
        {
            #region 這裡是拼裝---賦屬性值的代碼 
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            //MemberBinding: 就是一個表達式目錄樹
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())   //這裡是處理屬性的
            {
                MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));

                MemberBinding memberBinding = Expression.Bind(item, property);

                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TOut).GetFields()) //處理欄位的
            {
                MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());  //組裝了一個轉換的過程;

            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
            {
                parameterExpression
            });

            #endregion


            Func<TIn, TOut> func = lambda.Compile();//拼裝是一次性的
            _Dic[key] = func;
        }
        return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
    }
}
PeopleCopy peopleCopy4 = ExpressionMapper.Trans<People, PeopleCopy>(people);

表達式+反射+泛型類的方式

    public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
    {
        private static Func<TIn, TOut> _FUNC = null;
        static ExpressionGenericMapper()
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())
            {
                MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TOut).GetFields())
            {
                MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
            {
                    parameterExpression
            });
            _FUNC = lambda.Compile();//拼裝是一次性的
        }
        public static TOut Trans(TIn t)
        {
            return _FUNC(t);
        }
    }
}
PeopleCopy peopleCopy5 = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);

最後運行一百萬次,來看一下效率。

{
    People people = new People()
    {
        Id = 11,
        Name = "Richard",
        Age = 31
    };
    long common = 0;
    long generic = 0;
    long cache = 0;
    long reflection = 0;
    long serialize = 0;
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 1_000_000; i++)
        {
            PeopleCopy peopleCopy = new PeopleCopy()
            {
                Id = people.Id,
                Name = people.Name,
                Age = people.Age
            };
        }
        watch.Stop();
        common = watch.ElapsedMilliseconds;
    }
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 1_000_000; i++)
        {
            PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people);
        }
        watch.Stop();
        reflection = watch.ElapsedMilliseconds;
    }
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 1_000_000; i++)
        {
            PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people);
        }
        watch.Stop();
        serialize = watch.ElapsedMilliseconds;
    }
    {

        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 1_000_000; i++)
        {
            PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people);
        }
        watch.Stop();
        cache = watch.ElapsedMilliseconds;
    }
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 1_000_000; i++)
        {
            PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
        }
        watch.Stop();
        generic = watch.ElapsedMilliseconds;
    }

    Console.WriteLine($"common = {common} ms");        //性能最高,但是不能通用;
    Console.WriteLine($"reflection = {reflection} ms");
    Console.WriteLine($"serialize = {serialize} ms");
    Console.WriteLine($"cache = {cache} ms");
    Console.WriteLine($"generic = {generic} ms"); //性能好,而且擴展性也好===又要馬兒跑,又要馬兒不吃草。。。
}

看運行後的結果
img

核心:動態生成硬編碼;----代碼運行的時候生成了一段新的邏輯;

四、表達式樹和sql

為什麼要使用表達式目錄樹來拼裝解析呢?
可以提供重用性

如果封裝好一個方法,接受一個表達式樹,在解析的時候,其實就是不斷的訪問,訪問的時候,會按照固定的規則,避免出錯;

任何的一個表達式樹都可以用一個通用的方法解析並且支持泛型,更加容易去封裝;

例子:

需要的擴展類

 public class OperationsVisitor : ExpressionVisitor
 {
     public Expression Modify(Expression expression)
     {
         Console.WriteLine(expression.ToString()) ;

         //ExpressionVisitor:
         //1.Visit方法--訪問表達式目錄樹的入口---分辨是什麼類型的表達式目錄
         //2.調度到更加專業的方法中進一步訪問,訪問一遍之後,生成一個新的表達式目錄   ---有點像遞歸,不全是遞歸;
         //3.因為表達式目錄樹是個二叉樹,ExpressionVisitor一直往下訪問,一直到葉節點;那就訪問了所有的節點
         //4.在訪問的任何一個環節,都可以拿到對應當前環節的內容(參數名稱、參數值。。),就可以進一步擴展
         return this.Visit(expression);
     }

     /// <summary>
     /// 覆寫父類方法
     /// </summary>
     /// <param name="b"></param>
     /// <returns></returns>
     protected override Expression VisitBinary(BinaryExpression b)
     {
        
         if (b.NodeType == ExpressionType.Add)
         {
             Expression left = this.Visit(b.Left);
             Expression right = this.Visit(b.Right);
             return Expression.Subtract(left, right);
         }
         else if (b.NodeType==ExpressionType.Multiply) //如果是相乘
         {
             Expression left = this.Visit(b.Left);
             Expression right = this.Visit(b.Right);
             return Expression.Divide(left, right); //相除
         } 
         return base.VisitBinary(b);
     }

     /// <summary>
     /// 覆寫父類方法
     /// </summary>
     /// <param name="node"></param>
     /// <returns></returns>
     protected override Expression VisitConstant(ConstantExpression node)
     {
         return base.VisitConstant(node);
     }

對應的表達式解析

 Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
 OperationsVisitor visitor = new OperationsVisitor();
 //visitor.Visit(exp);
 Expression expNew = visitor.Modify(exp);

同時表達式樹中已經通過使用觀察者模式封裝好了Visit方法.

  1. Visit方法--訪問表達式樹的入口---分辨是什麼類型的表達式目錄
  2. 調度到更加專業的方法中進一步訪問,訪問一邊以後,生成一個新的表達式目錄. --- 有點像遞歸,不全是遞歸
  3. 因為表達式目錄樹是一個二叉樹,ExpreesionVistor一直往下訪問,一直到葉子節點;通過二叉樹的遍歷就訪問了所有的節點.
  4. 在訪問的任何一個環節,都可以拿到對應當前環節的內容(參數名稱、參數值...)就可以進一步擴展.

現在開始將表達式樹跟sql語句進行連接

例子:
擴展類

 public class ConditionBuilderVisitor : ExpressionVisitor
 {
     private Stack<string> _StringStack = new Stack<string>();

     public string Condition()
     {
         string condition = string.Concat(this._StringStack.ToArray());
         this._StringStack.Clear();
         return condition;
     }

     /// <summary>
     /// 如果是二元表達式
     /// </summary>
     /// <param name="node"></param>
     /// <returns></returns>
     protected override Expression VisitBinary(BinaryExpression node)
     {
         if (node == null) throw new ArgumentNullException("BinaryExpression");

         this._StringStack.Push(")");
         base.Visit(node.Right);//解析右邊
         this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
         base.Visit(node.Left);//解析左邊
         this._StringStack.Push("(");

         return node;
     }

     /// <summary>
     /// 解析屬性
     /// </summary>
     /// <param name="node"></param>
     /// <returns></returns>
     protected override Expression VisitMember(MemberExpression node)
     {
         if (node == null) throw new ArgumentNullException("MemberExpression");
         //this._StringStack.Push(" [" + node.Member.Name + "] ");
         ////return node; 
         if (node.Expression is ConstantExpression)
         {
             var value1 = this.InvokeValue(node);
             var value2 = this.ReflectionValue(node);
             //this.ConditionStack.Push($"'{value1}'");
             this._StringStack.Push("'" + value2 + "'");
         }
         else
         {
             this._StringStack.Push(" [" + node.Member.Name + "] ");
         }
         return node;
     }


     private object InvokeValue(MemberExpression member)
     {
         var objExp = Expression.Convert(member, typeof(object));//struct需要
         return Expression.Lambda<Func<object>>(objExp).Compile().Invoke();
     }

     private object ReflectionValue(MemberExpression member)
     {
         var obj = (member.Expression as ConstantExpression).Value;
         return (member.Member as FieldInfo).GetValue(obj);
     }

     /// <summary>
     /// 常量表達式
     /// </summary>
     /// <param name="node"></param>
     /// <returns></returns>
     protected override Expression VisitConstant(ConstantExpression node)
     {
         if (node == null) throw new ArgumentNullException("ConstantExpression");
         this._StringStack.Push(" '" + node.Value + "' ");
         return node;
     }
     /// <summary>
     /// 方法表達式
     /// </summary>
     /// <param name="m"></param>
     /// <returns></returns>
     protected override Expression VisitMethodCall(MethodCallExpression m)
     {
         if (m == null) throw new ArgumentNullException("MethodCallExpression");

         string format;
         switch (m.Method.Name)
         {
             case "StartsWith":
                 format = "({0} LIKE {1}+'%')";
                 break;

             case "Contains":
                 format = "({0} LIKE '%'+{1}+'%')";
                 break;

             case "EndsWith":
                 format = "({0} LIKE '%'+{1})";
                 break;

             default:
                 throw new NotSupportedException(m.NodeType + " is not supported!");
         }
         this.Visit(m.Object);
         this.Visit(m.Arguments[0]);
         string right = this._StringStack.Pop();
         string left = this._StringStack.Pop();
         this._StringStack.Push(String.Format(format, left, right));

         return m;
     }
 }

對應的sql語句的解析

{ 
    Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Id > 5
                                             && x.Name.StartsWith("1") //  like '1%'
                                             && x.Name.EndsWith("1") //  like '%1'
                                             && x.Name.Contains("1");//  like '%1%'

    string sql = string.Format("Delete From [{0}] WHERE [Age]>5 AND [ID] >5"
        , typeof(People).Name
        , " [Age]>5 AND [ID] >5");

    ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
    vistor.Visit(lambda);
    Console.WriteLine(vistor.Condition());
}
{
    //  ((( [Age] > '5') AND( [Name] =  [name] )) OR( [Id] > '5' )) 
    string name = "AAA";
    Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Name == name || x.Id > 5;
    ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
    vistor.Visit(lambda);
    Console.WriteLine(vistor.Condition());
}
{
    Expression<Func<People, bool>> lambda = x => x.Age > 5 || (x.Name == "A" && x.Id > 5);
    ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
    vistor.Visit(lambda);
    Console.WriteLine(vistor.Condition());
}
{
    Expression<Func<People, bool>> lambda = x => (x.Age > 5 || x.Name == "A") && x.Id > 5;
    ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
    vistor.Visit(lambda);
    Console.WriteLine(vistor.Condition());
}

在我自己的看法,使用表達式樹而不是傳統的方式去解析sql語句的優點

  1. 通過二叉樹的方式表達,更加的有條理性
  2. 使用泛型等技術更方式實現一個通用的sql語句的解析。
  3. 會有類型檢查,出錯後也能使用異常處理。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 距離2020年已經過去很久了,各大編譯器對於C++20各項標準的支持也日趨完善,無棧協程也是其中之一,所以我就嘗試著拿協程與`io_uring`實現了一下proactor模式,這篇文章用來記錄一下我的設計和想法。除此之外,我們能在網路上找到許多優秀的C++20協程的教程以及許多優秀的協程應用(庫),... ...
  • 本文分享自華為雲社區《構建大型Web應用Flask中的Blueprints指南》,作者: 檸檬味擁抱。 什麼是Blueprints? Blueprints是Flask中的一種模式,用於將應用程式分解為可重用的模塊。每個藍圖實際上是一個包含一組路由、視圖和靜態文件的Python模塊。通過使用藍圖,我們 ...
  • MoneyPrinterTurbo —— 一個利用大模型,一鍵生成短視頻的開源項目。只需輸入視頻主題或關鍵詞,就可以全自動生成視頻文案、視頻素材、視頻字幕、視頻背景音樂,最後合成一個高清的短視頻。 ...
  • 本文介紹基於R語言中的Ternary包,繪製三元圖(Ternary Plot)的詳細方法;其中,我們就以RGB三色分佈圖為例來具體介紹~ ...
  • 本文介紹瞭如何快速搭建一個基於大型語言模型(LLM)的混元聊天應用。強調了開發速度的重要性,並指出了使用Streamlit這一工具的優勢,特別是對於不熟悉前端代碼的開發者來說,Streamlit提供了一種快速構建聊天應用的方法。 ...
  • 前言 aardio中有些經常使用的庫,換個項目總需要複製一下,還不便於修改。雖然可以直接把它放到aardio\lib目錄下,也是不便於共用給其他人使用。 最近偶然翻到編輯器里的工具->開發環境->擴展庫發佈工具,就想著可以像官方一樣,發佈自己的擴展庫,也便於分享給大家使用,最好能像官方擴展庫一樣線上 ...
  • 隨著汽車的普及和使用頻率的增加,車輛的維修保養成為了車主們經常需要面對的問題。為了提供更好的服務,挖數據平臺提供了一個維修保養記錄統計介面,讓用戶可以方便地查詢車輛的保養記錄和維修記錄。本文將對該介面進行詳細解析,並介紹其使用方法和應用場景。 首先,我們來看一下該介面的具體功能。該介面可以查詢車輛的 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...