基於Expression Lambda表示式樹的通用複雜動態查詢構建器——《構思篇二》已開源

2023-05-27 09:00:44

接續[上篇之預告]

本篇來講講,如何根據前面設計的查詢描述器構造出可執行的表示式。正如標題所示,實現手段將採用Expression Lambda技術。

先來看看主角System.Linq.Expressions.Expression 長什麼樣,都有些什麼東西,能做什麼。 先看看它的類圖:

 

 

我們主要使用Expression這個類,它包含各種節點型別的 static工廠方法。特別是以下這些方法,對應了查詢需要用到的邏輯、比較、數學,等各種運算操作。

邏輯運算  
AndAlso(Expression, Expression) 表示And邏輯運算
OrElse(Expression, Expression) 表示Or邏輯運算
比較運算  
LessThan(Expression, Expression) 表示小於:"<"
LessThanOrEqual(Expression, Expression) 表示大於等於:「<="
GreaterThan(Expression, Expression) 表示大於:」>「
GreaterThanOrEqual(Expression, Expression) 表示大等於:」>="
Equal(Expression, Expression) 表示等於:」=「
NotEqual(Expression, Expression) 表示不等於:」!="
IsTrue(Expression) 測試表示式結果是否為「True"
IsFalse(Expression) 測試表示式結果是否為「False"
Not(Expression) 表示」Not"
數學運算  
Add(Expression, Expression) 加法
Subtract(Expression, Expression) 減法
Multiply(Expression, Expression) 乘法
Divide(Expression, Expression) 除法
Modulo(Expression, Expression) 模除
Power(Expression, Expression) 冪運算
一些操作  
Parameter(Type) 包裝一個型別為Type的引數
Call(Expression, MethodInfo, Expression, Expression) 包裝一個方法呼叫
Bind(MemberInfo, Expression) 包裝一個成員繫結,(屬性或欄位)
Quote(Expression) 包裝一個括號
MakeUnary(ExpressionType, Expression, Type) 包裝一個一元運算
MakeBinary(ExpressionType, Expression, Expression) 包裝一個二元運算
TypeAs(Expression, Type) 包裝一個顯式參照或裝箱轉換,其中如果轉換失敗則提供「null」。
Coalesce(Expression, Expression, LambdaExpression) 包裝表示給定轉換函數的聚結操作。
變數、常數、欄位、屬性  
Default(Type) 包裝一個指定型別的預設值
Constant(Object, Type) 包裝一個常數
Assign(Expression, Expression) 賦值操作
Variable(Type, String) 包裝一個變數定義
Property(Expression, MethodInfo) 包裝一個屬性
Field(Expression, FieldInfo) 包裝一個欄位
PropertyOrField(Expression, String) 包裝一個指定名稱的欄位或屬性
要的就是這結果  
Lambda(Expression, ParameterExpression[]) 包裝一個委託,帶有一個參數列達式陣列。

以上僅列出了一部分,它涵蓋了全部可能要使用到的函數,語句,欲詳細瞭解可以直接乘坐火箭到微軟官方網站檢視。

這此函數或功能是有了,如何使用呢?來看個例子:將SQL:Table1.A > 5 and Table1.B=3,轉成(Table1)=>Table1.A > 5 && Table1.B==3;

Expression的世界裡一切都是Expression。因此首先要將Table1A5B3先包裝成Expression

  • 開幹之前先談個物件
//實體類:
    public class Table
    {
        public int A;
        public int B;
    }

 

  • 然後包裝各種Expression
​
    //將Table1包裝成ParameterExpression:
    var p=Expression.Parameter(typeof(Table1),"Table1");
​
    //將5、3這兩個常數包裝成ConstantExpression:
    var num5=Expression.Constant(5,typeof(int));
    var num3=Expression.Constant(3,typeof(int));
​
    //將兩個屬性包裝成MemberExpression。
    var a=Expression.PropertyOrField(p,"A")
    var b=Expression.PropertyOrField(p,"B")
​
    //構造Table1.A>5:
    var gt=Expression.GreaterThen(a,num5); 
    //構造Table1.A=3:
    var eq=Expression.Equal(b,num3);    
    
​

 

  • 再構造兩個比較式
   //構造Table1.A>5:
   var gt=Expression.GreaterThen(a,num5); 
    //構造Table1.A=3:
    var eq=Expression.Equal(b,num3);    

 

  •  加上邏輯And將兩個比較式連線起來
    //構造Table1.A>5 && Table1.A=3
    var exp=Expression.AndAlso(gt,eq);

 

  •  結果就要出來了
//結果就獲得了:
    var lambda=Expression.Lambda()
        

 

  • 來來來,測試一下康康接果:
    /*===============來來來,測試一下:===============*/    
    var f=lambda.Compile();
    var x=f.DynamicInvoke(new Table(){A=6,B=3});
    var y = f.DynamicInvoke(new Table() { A = 2, B = 3 });
    Console.WriteLine("x:{0}\ny:{1}",x,y);
    Console.WriteLine("Lambda:{0}",lambda.ToString());          
    /*輸出:
    結果對嗎?我也不知道,我擼得這麼辛苦,先讓電腦休息一下,各位看官只要Ctrl+C,Ctrl+V就可以看到了.
    */

 

  • OK,其它各種操作,只要依葫蘆畫瓢便大功可成! 先爽一下!
  • 好,暫且爽到這裡,欲看如何把括號弄成俄羅斯套娃,且看下回分解.
  • 隨手點個贊,讓我也爽爽,可好!