公司的orm框架在dapper的基礎上擴展了一套表達式的方法,當時就研究了一下,把學習過程和結果記錄下來,和你們分享。node
有人會說重複造輪子不必,直接上EF。sql
從個人角度來看重複造輪子的緣由有如下三種:express
一、研究造輪子的原理app
二、輪子不知足如今的開發須要框架
三、裝Bide
最經常使用到的無非就是ORM的刪查改的條件,ORM就是在ado.Net的基礎上封裝了一層表達式,最後仍是將表達式解析成sql,由ado.Net去執行。學習
那麼咱們能將表達式樹解析成字符串,那麼也能反過來。例如運費系統,在後臺設置定義好一套計算規則。例如:對應不一樣的發貨渠道,什麼重量取哪一個區間的費用,多於哪一個階段的費用還要額外費用。咱們能夠經過解析這套計算規則拼裝好表達式樹傳入參數進行計算。。。ui
還有別的在評論補充下。。。this
不扯多,如今咱們只拿解析表達式樹來學習。spa
首先建立4個屬性的Users類
1 namespace CG.ExpressionProject 2 { 3 /// <summary> 4 /// 用戶類 5 /// </summary> 6 public class Users 7 { 8 public string Name { get; set; } 9 10 public int Phone { get; set; } 11 12 public int Sex { get; set; } 13 14 public int Age { get; set; } 15 } 16 }
接着,咱們從最簡單的開始,寫一個二元運算表達式,F5調試監控觀察。
Expression<Func<Users, bool>> expressionUser = users => users.Name == "SkyChen"
從上圖能夠看見有不少屬性,在表達式主體(屬性Body),咱們暫時只關注三個屬性,Left(左節點)、Right(右節點)和 NodeType (當前節點類型)
表達式主體(users.Name == "SkyChen")是一個二元運算表達式,所以能夠將Body轉換成 BinaryExpression 類型來訪問Left和Right。
Left 和 Right 的 NodeType 分別爲 MemberAccess(從字段或屬性進行讀取的運算)、Constant(常量)。
所以能夠將 Left 轉換成 MemberExpression 類型來訪問 Member 屬性,將 Right 轉換成 ConstantExpression 類型來訪問 Value 屬性。具體代碼以下:
public static string ResolveExpression(Expression<Func<Users, bool>> expression) { var bodyNode = (BinaryExpression)expression.Body; var leftNode = (MemberExpression)bodyNode.Left; var rightNode = (ConstantExpression)bodyNode.Right; return string.Format(" {0} {2} {1} ", leftNode.Member.Name, rightNode.Value, bodyNode.NodeType.TransferExpressionType()); }
TransferExpressionType 是針對部分 ExpressionType 的一個轉換。
public static string TransferExpressionType(this ExpressionType expressionType) { string type = ""; switch (expressionType) { case ExpressionType.Equal: type = "="; break; case ExpressionType.GreaterThanOrEqual: type = ">="; break; case ExpressionType.LessThanOrEqual: type = "<="; break; case ExpressionType.NotEqual: type = "!="; break; case ExpressionType.AndAlso: type = "And"; break; case ExpressionType.OrElse: type = "Or"; break; } return type; }
那麼。一個最簡單的表達式解析成where語句就完成了。
然而,實踐工做中,你們都會寫相對複雜或者說多個條件的表達式。那麼再採用上面的方式是沒法確認表達式節點的類型進行轉換的。咱們能夠添加一個Visit方法,根據 NodeType 轉換成對應的Expression的類型,從而方法訪問對應的屬性進行表達式解析。
可是,重寫以前,咱們得了解一件事,既然叫表達式樹,意味着在子節點裏,還會有多個節點,以下圖:
那麼,咱們假設,只要是 BinaryExpression(二元運算表達式)就會有多個子節,去訪問子節點就是一個遞歸的過程,而終點就是 MemberExpression 和 ConstantExpression,對應字段名稱和常量值的拼接。
下面是代碼實現:
public class ExpressionTypeHelper { public StringBuilder GeWhere = new StringBuilder(100); public string Where { get { return GeWhere.ToString(); } } public void ResolveExpression(Expression<Func<Users, bool>> expression) { Visit(expression.Body); } public void Visit(Expression expression) { switch (expression.NodeType) { case ExpressionType.Constant: VisitConstantExpression(expression); break; case ExpressionType.MemberAccess: VisitMemberExpression(expression); break; case ExpressionType.Convert: VisitUnaryExpression(expression); break; default: VisitBinaryExpression(expression); break; } } public void VisitUnaryExpression(Expression expression) { var e = (UnaryExpression)expression; Visit(e.Operand); } public void VisitBinaryExpression(Expression expression) { var e = (BinaryExpression)expression; GeWhere.Append("("); Visit(e.Left); GeWhere.Append(e.NodeType.TransferExpressionType()); Visit(e.Right); GeWhere.Append(")"); } public void VisitConstantExpression(Expression expression) { var e = (ConstantExpression)expression; if (e.Type == typeof(string)) { GeWhere.Append("'" + e.Value + "'"); } else { GeWhere.Append(e.Value); } } public void VisitMemberExpression(Expression expression) { var e = (MemberExpression)expression; GeWhere.Append(e.Member.Name); } }
結果以下:
一個基本的表達式解析思路基本實現了,可是!隨着本身的orm的完善是否是這麼多種的Expression類型都得在Visit方法添一遍,不是的。
ExpressionVisitor類是提供給咱們的表達式樹解析的幫助類,咱們只要定義一個類繼承ExpressionVisitor,實現一個 ResolveExpression 入口方法,重寫
VisitBinary、VisitConstant、VisitMember方法,代碼以下:
public class ExpressionTrasfer : ExpressionVisitor { public StringBuilder GeWhere = new StringBuilder(100); public string Where { get { return GeWhere.ToString(); } } public void ResolveExpression(Expression<Func<Users, bool>> expression) { Visit(expression.Body); } protected override Expression VisitBinary(BinaryExpression node) { GeWhere.Append("("); Visit(node.Left); GeWhere.Append(node.NodeType.TransferExpressionType()); Visit(node.Right); GeWhere.Append(")"); return node; } protected override Expression VisitConstant(ConstantExpression node) { if (node.Type == typeof(string)) { GeWhere.Append("'" + node.Value + "'"); } else if (node.Type == typeof(int)) { GeWhere.Append(node.Value); } return node; } protected override Expression VisitMember(MemberExpression node) { GeWhere.Append(node.Member.Name); return node; } }
一個簡單的表達式解析大體完成了,固然裏面還有不少能夠完善,例如值類型的判斷,is 仍是 = ,VisitMethodCall重寫等等。原理就這樣,實現我這裏就不一一列舉。如對你們有幫助,麻煩請推薦,有不足請在下面評論提出,我會一一更改。