国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

干貨!表達式樹解析"框架"(3)

2019-11-14 16:51:53
字體:
來源:轉載
供稿:網友

最新設計請移步 輕量級表達式樹解析框架Faller http://m.survivalescaperooms.com/blqw/p/Faller.html

 

 

  這應該是年前最后一篇了,接下來的時間就要陪陪老婆孩子了

  關于表達式樹解析也是最后一篇了,該說到的中心思想都已經說到了,理解接受能力比較好的童鞋應該已經可以舉一反三了

  研究表達式樹的過程讓我感覺微軟的設計真的是非常的巧妙,也為今后我的開發之路增添了新的思路

  好了 廢話不多說了 這篇主要是為了解決上篇中的提問的

聲明

  解決問題的辦法有很多,我只是根據我的個人習慣和風格介紹我的解決方案,并不一定就是最好的,僅僅只是提供一種思路,大家可以根據自己或項目的實際情況酌情對待

  關于問題請參考干貨!表達式樹解析"框架"(2)結尾

問題一

db.Where<User>(u => u.Name != null);            //u.Name is not null  而非( u.Name <> null )

分析

  這個問題主要是在Sql中`二元表達式`有一個非常特別的情況,如果和null進行比較,那么應該用is或is not 而不是=或者<>(!=)

  so~我的做法是在解析二元表達式的類中處理,如第二個參數是null,且符號是Equals或NotEqual,則使用is/is not

  怎么判斷第二個參數是null?

  這里我打算直接判斷ParserArgs.Builder中最后5個字符,如果是 " NULL" 就算是NULL了

  但是這里有個問題,就是原來的操作是先加入符號,再加入Right的,所以這里也要改,改為先放入Right再插入符號

代碼如下

    class BinaryExPRessionParser : ExpressionParser<BinaryExpression>    {        public override void Where(BinaryExpression expr, ParserArgs args)        {            if (ExistsBracket(expr.Left))            {                args.Builder.Append(' ');                args.Builder.Append('(');                Parser.Where(expr.Left, args);                args.Builder.Append(')');            }            else            {                Parser.Where(expr.Left, args);            }            var index = args.Builder.Length;            if (ExistsBracket(expr.Right))            {                args.Builder.Append(' ');                args.Builder.Append('(');                Parser.Where(expr.Right, args);                args.Builder.Append(')');            }            else            {                Parser.Where(expr.Right, args);            }            var length = args.Builder.Length;            if (length - index == 5 &&                 args.Builder[length - 5] == ' ' &&                 args.Builder[length - 4] == 'N' &&                 args.Builder[length - 3] == 'U' &&                 args.Builder[length - 2] == 'L' &&                 args.Builder[length - 1] == 'L')            {                Sign(expr.NodeType, index, args, true);            }            else            {                Sign(expr.NodeType, index, args);            }        }        /// <summary> 判斷是否需要添加括號        /// </summary>        private static bool ExistsBracket(Expression expr)        {            var s = expr.ToString();            return s != null && s.Length > 5 && s[0] == '(' && s[1] == '(';        }        private static void Sign(ExpressionType type, int index, ParserArgs args, bool useis = false)        {            switch (type)            {                case ExpressionType.And:                case ExpressionType.AndAlso:                    args.Builder.Insert(index, " AND");                    break;                case ExpressionType.Equal:                    if (useis)                    {                        args.Builder.Insert(index, " IS");                    }                    else                    {                        args.Builder.Insert(index, " =");                    }                    break;                case ExpressionType.GreaterThan:                    args.Builder.Insert(index, " >");                    break;                case ExpressionType.GreaterThanOrEqual:                    args.Builder.Insert(index, " >=");                    break;                case ExpressionType.NotEqual:                    if (useis)                    {                        args.Builder.Insert(index, " IS NOT");                    }                    else                    {                        args.Builder.Insert(index, " <>");                    }                    break;                case ExpressionType.Or:                case ExpressionType.OrElse:                    args.Builder.Insert(index, " OR");                    break;                case ExpressionType.LessThan:                    args.Builder.Insert(index, " <");                    break;                case ExpressionType.LessThanOrEqual:                    args.Builder.Insert(index, " <=");                    break;                default:                    throw new NotImplementedException("無法解釋節點類型" + type);            }        }        ... ...    }

結果

db.Where<User>(u => u.Name != null);//打印 SELECT * FROM [User] u WHERE u.[Name] IS NOT NULL

問題二

db.Where<User>(u => u.Name.StartsWith("bl"));   //u.Name like 'bl%'

分析

  這2個表達式只要運行一下就可以知道,他們是無法被解析的,原因就是:

  

  尚未實現MethodCallExpression的解析

  因為2個都屬性MethodCall表達式

  所以只需要實現MethodCallExpressionParser即可

  MethodCallExpression 方法調用表達式

  Method 表示調用的方法  

  Arguments 表示方法中用到的參數

  Object 表示調用方法的實例對象

  每種方法對應的解析都是不同的,所以我為每個方法都實現一個單獨的解析函數

  比如String類中的3個操作,分別對應3種Like的情況

        public static void String_StartsWith(MethodCallExpression expr, ParserArgs args)        {                     }        public static void String_Contains(MethodCallExpression expr, ParserArgs args)        {        }        public static void String_EndsWith(MethodCallExpression expr, ParserArgs args)        {        }

   然后將他們加入到一個鍵值對中(因為方法是有重載的,所以會有一樣名字的方法,但是解析方式是相同的)

        static Dictionary<MethodInfo, Action<MethodCallExpression, ParserArgs>> _Methods = MethodDitcInit();        private static Dictionary<MethodInfo, Action<MethodCallExpression, ParserArgs>> MethodDitcInit()        {            Dictionary<MethodInfo, Action<MethodCallExpression, ParserArgs>> dict = new Dictionary<MethodInfo, Action<MethodCallExpression, ParserArgs>>();            var type = typeof(string);            foreach (var met in type.GetMethods())            {                switch (met.Name)                {                    case "StartsWith":                        dict.Add(met, String_StartsWith);                        break;                    case "Contains":                        dict.Add(met, String_Contains);                        break;                    case "EndsWith":                        dict.Add(met, String_EndsWith);                        break;                    default:                        break;                }            }            return dict;        }

 調用where

        public override void Where(MethodCallExpression expr, ParserArgs args)        {            Action<MethodCallExpression, ParserArgs> act;            if (_Methods.TryGetValue(expr.Method,out act))            {                act(expr, args);                return;            }            throw new NotImplementedException("無法解釋方法" + expr.Method);        }

  現在分別完成3個String_的函數就可以了

        public static void String_StartsWith(MethodCallExpression expr, ParserArgs args)        {            Parser.Where(expr.Object, args);            args.Builder.Append(" LIKE");            Parser.Where(expr.Arguments[0], args);            args.Builder.Append(" + '%'");        }        public static void String_Contains(MethodCallExpression expr, ParserArgs args)        {            Parser.Where(expr.Object, args);            args.Builder.Append(" LIKE '%' +");            Parser.Where(expr.Arguments[0], args);            args.Builder.Append(" + '%'");        }        public static void String_EndsWith(MethodCallExpression expr, ParserArgs args)        {            Parser.Where(expr.Object, args);            args.Builder.Append(" LIKE '%' +");            Parser.Where(expr.Arguments[0], args);        }

 結果

db.Where<User>(u => u.Name.StartsWith("bl"));   db.Where<User>(u => u.Name.Contains("bl"));   db.Where<User>(u => u.Name.EndsWith("bl"));  /*打印SELECT * FROM [User] u WHERE u.[Name] LIKE 'bl' + '%'SELECT * FROM [User] u WHERE u.[Name] LIKE '%' + 'bl' + '%'SELECT * FROM [User] u WHERE u.[Name] LIKE '%' + 'bl'*/

  

問題三

int[] arr = { 13, 15, 17, 19, 21 };db.Where<User>(u => arr.Contains(u.Age));        //u.Age in (13,15,17,19,21)

分析

  這個問題和剛才那個問題有很多相似之處,所以首先需要在MethodCallExpressionParser類中實現一個對應Enumerable.Contains的解析函數

  但是這個方法有一個比較特殊的地方就是 他是泛型方法, 所以在從鍵值對中獲取處理函數的時候,需要把他轉為`泛型方法定義`(MethodInfo.GetGenericMethodDefinition)的才可以

        public override void Where(MethodCallExpression expr, ParserArgs args)        {            Action<MethodCallExpression, ParserArgs> act;            var key = expr.Method;            if (key.IsGenericMethod)            {                key = key.GetGenericMethodDefinition();            }             if (_Methods.TryGetValue(key, out act))            {                act(expr, args);                return;            }            throw new NotImplementedException("無法解釋方法" + expr.Method);        }

  對應的處理函數

        public static void Enumerable_Contains(MethodCallExpression expr, ParserArgs args)        {            Parser.Where(expr.Arguments[1], args);            args.Builder.Append(" IN");            Parser.Where(expr.Arguments[0], args);        }

  看上去似乎已經完成了,但是結果是....

int[] arr = { 13, 15, 17, 19, 21 };db.Where<User>(u => arr.Contains(u.Age));//打印//SELECT * FROM [User] u WHERE u.[Age] IN 'Demo.Program+<>c__DisplayClass0'[arr]

問題

  問題在于arr和u.Age一樣都是MemberExpression,而MemberExpression的解析之前是這樣寫的

    class MemberExpressionParser:ExpressionParser<MemberExpression>    {        public override void Where(MemberExpression expr, ParserArgs args)        {            Parser.Where(expr.Expression, args);            args.Builder.Append('[');            args.Builder.Append(expr.Member.Name);            args.Builder.Append(']');        }        ... ...    }

  顯然MemberExpression有兩種,一種是`虛擬的`,是不存在值的,比如u.Age,

  還有一種是真實的比如上面例子中的arr,他是有真實值的

  所以這個地方要改一改

代碼

  這塊地方比較難,需要理解一下

    class MemberExpressionParser : ExpressionParser<MemberExpression>    {        public override void Where(MemberExpression expr, ParserArgs args)        {            if (expr.Expression is ParameterExpression)            {                Parser.Where(expr.Expression, args);                args.Builder.Append('[');                args.Builder.Append(expr.Member.Name);                args.Builder.Append(']');            }            else            {                object val = GetValue(expr);                args.Builder.Append(' ');                IEnumerator array = val as IEnumerator;                if (array != null)                {                    AppendArray(args, array);                }                else if(val is IEnumerable)                {                    AppendArray(args, ((IEnumerable)val).GetEnumerator());                }                else                {                    AppendObject(args, val);                }              }        }        /// <summary> 獲取成員表達式中的實際值        /// </summary>        private static object GetValue(MemberExpression expr)        {            object val;            var field = expr.Member as FieldInfo;            if (field != null)            {                val = field.GetValue(((ConstantExpression)expr.Expression).Value);            }            else            {                val = ((PropertyInfo)expr.Member).GetValue(((ConstantExpression)expr.Expression).Value, null);            }            return val;        }        /// <summary> 追加可遍歷對象(數組或集合或簡單迭代器)        /// </summary>        private static void AppendArray(ParserArgs args, IEnumerator array)        {            if (array.MoveNext())            {                args.Builder.Append('(');                AppendObject(args, array.Current);                while (array.MoveNext())                {                    args.Builder.Append(',');                    AppendObject(args, array.Current);                }                args.Builder.Append(')');            }            else            {                args.Builder.Append("NULL");            }        }        /// <summary> 追加一般對象        /// </summary>        public static void AppendObject(ParserArgs args, object val)        {            if (val == null || val == DBNull.Value)            {                args.Builder.Append("NULL");            }            else if (val is bool)            {                args.Builder.Append(val.GetHashCode());            }            else            {                var code = (int)Type.GetTypeCode(val.GetType());                if (code >= 5 && code <= 15)            //如果expr.Value是數字類型                {                    args.Builder.Append(val);                }                else                {                    args.Builder.Append('/'');                    args.Builder.Append(val);                    args.Builder.Append('/'');                }            }        }        ... ...    }

 結果

int[] arr = { 13, 15, 17, 19, 21 };db.Where<User>(u => arr.Contains(u.Age));//打印//SELECT * FROM [User] u WHERE u.[Age] IN (13,15,17,19,21)

問題四

如果需要使用參數化傳遞參數,又需要怎樣修改源碼呢?

分析

  其實這個問題是最簡單的一個問題,如果已經理解這個`框架`的工作原理可以輕松解決這個問題

代碼

  1.修改ParserArgs,使其中包含一個SqlParamete的集合,并且為了方便操作,將AppendObject的方法也移入ParserArgs,變為AddParameter

  使用參數化傳遞還有一個好處 可以不用判斷參數類型,來確定是否添加 單引號(')

    public class ParserArgs    {        public ParserArgs()        {            Builder = new StringBuilder();            SqlParameters = new List<SqlParameter>();        }                public List<SqlParameter> SqlParameters { get; set; }        public StringBuilder Builder { get; private set; }        /// <summary> 追加參數        /// </summary>        public void AddParameter(object obj)        {            if (obj == null || obj == DBNull.Value)            {                Builder.Append("NULL");            }            else            {                string name = "p" + SqlParameters.Count;                SqlParameters.Add(new SqlParameter(name, obj));                Builder.Append('@');                Builder.Append(name);            }        }    }

  2.修改本來應該輸出值的位置,改為輸出參數名,并將參數加入集合

  ConstantExpressionParser.Where

        public override void Where(ConstantExpression expr, ParserArgs args)        {            args.Builder.Append(' ');            var val = expr.Value;            if (val == null || val == DBNull.Value)            {                args.Builder.Append("NULL");                return;            }            if (val is bool)            {                args.Builder.Append(val.GetHashCode());                return;            }            var code = (int)Type.GetTypeCode(val.GetType());            if (code >= 5 && code <= 15)            //如果expr.Value是數字類型            {                args.Builder.Append(val);            }            else            {                args.Builder.Append('/'');                args.Builder.Append(val);                args.Builder.Append('/'');            }        }
原方法

  改為

        public override void Where(ConstantExpression expr, ParserArgs args)        {            args.Builder.Append(' ');            args.AddParameter(expr.Value);        }

  MemberExpressionParser.AppendObject

        /// <summary> 追加一般對象        /// </summary>        public static void AppendObject(ParserArgs args, object val)        {            if (val == null || val == DBNull.Value)            {                args.Builder.Append("NULL");            }            else if (val is bool)            {                args.Builder.Append(val.GetHashCode());            }            else            {                var code = (int)Type.GetTypeCode(val.GetType());                if (code >= 5 && code <= 15)            //如果expr.Value是數字類型                {                    args.Builder.Append(val);                }                else                {                    args.Builder.Append('/'');                    args.Builder.Append(val);                    args.Builder.Append('/'');                }            }        }
原方法

  改為,    當然你也可以考慮刪除這個方法

        /// <summary> 追加一般對象        /// </summary>        public static void AppendObject(ParserArgs args, object val)        {            args.AddParameter(val);        }

  最后,調用方式進行一些修改

        public DataSet Where<T>(Expression<Func<T, bool>> expr)        {            var sql = "SELECT * FROM [" + typeof(T).Name + "] ";            ParserArgs a = new ParserArgs();            Parser.Where(expr.Body, a);            sql += expr.Parameters[0].Name + " WHERE" + a.Builder.ToString();            Console.WriteLine(sql);            using (var adp = new SqlDataAdapter(sql, ConnectionString))            {                adp.SelectCommand.Parameters.AddRange(a.SqlParameters.ToArray());//添加這一句                DataSet ds = new DataSet();                adp.Fill(ds);                return ds;            }        }

結果

ORM db = new ORM("server=192.168.0.96;database=tempdb;uid=sa;pwd=123456");db.Where<User>(u => u.Age > 18 && (u.Sex == true || u.Name == "blqw"));db.Where<User>(u => u.Name != null);db.Where<User>(u => u.Name.StartsWith("bl"));db.Where<User>(u => u.Name.Contains("bl"));db.Where<User>(u => u.Name.EndsWith("bl"));int[] arr = { 13, 15, 17, 19, 21 };db.Where<User>(u => arr.Contains(u.Age));/*打印SELECT * FROM [User] u WHERE u.[Age] > @p0 AND ( u.[Sex] = @p1 OR u.[Name] = @p2)SELECT * FROM [User] u WHERE u.[Name] IS NOT NULLSELECT * FROM [User] u WHERE u.[Name] LIKE @p0 + '%'SELECT * FROM [User] u WHERE u.[Name] LIKE '%' + @p0 + '%'SELECT * FROM [User] u WHERE u.[Name] LIKE '%' + @p0SELECT * FROM [User] u WHERE u.[Age] IN (@p0,@p1,@p2,@p3,@p4)*/

源碼下載

ExpressionParser3.rar

結束語

  關于表達式樹的解析已經全部講完了,自己回頭看看,如果沒有一定功力確實看起來比較費力

  雖然我已經將我知道的內容基本寫出來了,不過由于表達能力有限的緣故,所以可能還有很多人看不懂吧

  關于表達能力的問題,我只能說抱歉了,今后慢慢改進吧

  還是那句話,如果看完覺得有不明白的地方可以跟帖提問,我有空都會回答的,如果看完覺得完全看不懂,那我就沒辦法了...

  最后,希望大家過個好年,我自己也要過個好年,年前不發文章了,哈哈

 相關鏈接

  干貨!表達式樹解析"框架"(1) 

  干貨!表達式樹解析"框架"(2) 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 青龙| 城口县| 锡林郭勒盟| 梅州市| 十堰市| 洛扎县| 辰溪县| 台东市| 新蔡县| 甘德县| 临高县| 沁阳市| 玛沁县| 泽州县| 汕头市| 沧州市| 双牌县| 梁平县| 龙门县| 柳州市| 乌鲁木齐县| 潍坊市| 花垣县| 开阳县| 洛浦县| 楚雄市| 汉寿县| 萍乡市| 竹北市| 蕲春县| 石柱| 手游| 沙洋县| 双江| 绥化市| 忻城县| 阿坝县| 蒲城县| 临夏市| 平果县| 额尔古纳市|