表達式樹的解析.

前言

公司的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 }
View Code

 

接着,咱們從最簡單的開始,寫一個二元運算表達式,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());
        }
View Code

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;
        }
View Code

那麼。一個最簡單的表達式解析成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);
        }
    }
View Code

 結果以下:

ExpressionVisitor的使用

一個基本的表達式解析思路基本實現了,可是!隨着本身的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;
        }
    }
View Code

 

結束

一個簡單的表達式解析大體完成了,固然裏面還有不少能夠完善,例如值類型的判斷,is 仍是 = ,VisitMethodCall重寫等等。原理就這樣,實現我這裏就不一一列舉。如對你們有幫助,麻煩請推薦,有不足請在下面評論提出,我會一一更改。

相關文章
相關標籤/搜索