您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關C#中如何實現表達式目錄樹Expression的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
表達式目錄樹:語法樹,或者說是一種數據結構
1.表達式目錄樹Expression:System.Linq.Expressions;
2.描述了多個變量或者和常量之間的關系,按照一定的規則進行組裝!
可以向委托一樣使用lambd表達式快捷聲明;
不能有語句體,聲明只能有一行代碼;
可以通過Compile(),編譯成一個委托;
Func<int, int, int> func = (m, n) => { int i = 0; return m * n + 2; }; //委托 拉姆達表達式其實是作為委托的一個參數,本質是一個方法(匿名方法) Expression<Func<int, int, int>> exp = (m, n) => m * n + 2; //數據結構--就像對一個計算做了一個精確的描述,展開之后發現,分為左邊,右邊,每個元素都可以把值都獲取出來,二叉樹 var erpPlu= exp.Compile();//表達式目錄樹可以通過compile 轉換成一個委托 //表達式目錄樹:語法樹,或者說是一種數據結構 int iResult1 = func.Invoke(12, 23); int iResult2 = exp.Compile().Invoke(12, 23);
手動拼裝表達式目錄樹,不是用的lambda的快捷方式
//表達式目錄樹的拼裝 Expression<Func<int>> expression = () => 123 + 234; //兩個常量相加-----表達式目錄樹的快捷聲明 Expression constant123 = Expression.Constant(123); Expression constant234 = Expression.Constant(234); Expression expressionAdd = Expression.Add(constant123, constant234); var exp = Expression.Lambda<Func<int>>(expressionAdd); var func = exp.Compile(); int iResult = func.Invoke();
Expression<Func<int, int, int>> exp = (m, n) => m * n + m + n + 2; //快捷聲明--其實編譯器提供的便捷功能---語法糖--具體實現可通過反編譯工具查看 //具體實現可通過反編譯工具查看 ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m"); ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n"); Expression expContant2 = Expression.Constant(2, typeof(int)); Expression multipley = Expression.Multiply(parameterExpression, parameterExpression2); Expression expAdd = Expression.Add(multipley, parameterExpression); Expression expAdd1 = Expression.Add(expAdd, parameterExpression2); Expression expAdd2 = Expression.Add(expAdd1, expContant2); Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(expAdd2, new ParameterExpression[] { parameterExpression, parameterExpression2 }); Func<int, int, int> fun = expression.Compile(); int iResult = fun.Invoke(10, 11);
var peopleQuery = new List<People>().AsQueryable(); Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5"); peopleQuery.Where(lambda); ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x"); FieldInfo idfield = typeof(People).GetField("Id"); var idExp = Expression.Field(parameterExpression, idfield); MethodInfo toString = typeof(int).GetMethod("ToString", new Type[0]); var toStringExp = Expression.Call(idExp, toString, Array.Empty<Expression>()); var Equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) }); Expression expressionConstant5 = Expression.Constant("5", typeof(string)); var equalsExp = Expression.Call(toStringExp, Equals, new Expression[] { expressionConstant5 }); Expression<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(equalsExp, new ParameterExpression[] { parameterExpression }); Func<People, bool> func = expression.Compile(); var bResult = func.Invoke(new People() { Id = 5, Name = "海貝" }); new List<People>().AsQueryable().Where(expression);
var dbSet = new List<People>().AsQueryable();//EF DbSet dbSet.Where(p => p.Age == 25 & p.Name.Contains("陽光下的微笑")); Expression<Func<People, bool>> exp = null; Console.WriteLine("用戶輸入個名稱,為空就跳過"); string name = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(name)) { exp = p => p.Name.Contains(name); } Console.WriteLine("用戶輸入個最小年紀,為空就跳過"); string age = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge)) { exp = p => p.Age > iAge; }
上面的玩法是不是只有最后一個條件才生效?如果需要多個條件都滿足;怎么辦? 當然是拼裝啊;
拼裝可以從最小粒度來組裝表達式目錄樹;如果有一個封裝,你把各種條件給我,我從最小粒度開始一個一個的拼裝起來,不就是一個長的表達式目錄樹了嗎?
解決方案:
調用方可以組裝一個很長的表達式目錄樹傳遞過來;
表達式目錄樹傳遞過來以后,在這里應該做什么?應該解析;
所有信息都在表達式目錄樹里面,自然也可以把他解析(找出來)
解析就可以通過ExpressionVisitor解析----生成對應的Sql語句;
表達式目錄樹的訪問者----訪問者模式;
1.Visit方法–訪問表達式目錄樹的入口—分辨是什么類型的表達式目錄
2.調度到更加專業的方法中進一步訪問,訪問一遍之后,生成一個新的表達式目錄 —有點像遞歸,不全是遞歸;
3.因為表達式目錄樹是個二叉樹,ExpressionVisitor一直往下訪問,一直到葉節點;那就訪問了所有的節點;
4.在訪問的任何一個環節,都可以拿到對應當前環節的內容(參數名稱、參數值。。),就可以進一步擴展;
為什么要使用表達式目錄樹來拼裝解析呢:
1.可以提高重用性;
2.如果封裝好一個方法,接受一個表達式目錄樹,在解析的時候,其實就是不斷的訪問,訪問有規則;
3.任何一個表達式目錄樹都可以調用當前方法來解析;
4.表達式目錄樹可以支持泛型;
{ 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()); }
自己封裝的解析器,這就是EF6的底層原理,根據表達式樹自動生成相應的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; } }
internal static class SqlOperator { internal static string ToSqlOperator(this ExpressionType type) { switch (type) { case (ExpressionType.AndAlso): case (ExpressionType.And): return "AND"; case (ExpressionType.OrElse): case (ExpressionType.Or): return "OR"; case (ExpressionType.Not): return "NOT"; case (ExpressionType.NotEqual): return "<>"; case ExpressionType.GreaterThan: return ">"; case ExpressionType.GreaterThanOrEqual: return ">="; case ExpressionType.LessThan: return "<"; case ExpressionType.LessThanOrEqual: return "<="; case (ExpressionType.Equal): return "="; default: throw new Exception("不支持該方法"); } } }
表達式目錄樹動態拼接的實現方式:
/// <summary> /// 合并表達式 And Or Not擴展 /// </summary> 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); 錯誤的寫法,兩個表達式不是同一個參數 //將兩個表達式的參數統一為參數c 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); } }
/// <summary> /// 建立新表達式 /// </summary> 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; } }
調用方如下:
{ 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();//非 Do1(lambda3); Do1(lambda4); Do1(lambda5); } private static void Do1(Expression<Func<People, bool>> func) { List<People> people = new List<People>() { new People(){Id=4,Name="123",Age=4}, new People(){Id=5,Name="234",Age=5}, new People(){Id=6,Name="345",Age=6}, }; List<People> peopleList = people.Where(func.Compile()).ToList(); }
對象深拷貝
硬編碼
PeopleCopy peopleCopy = 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 propIn = tIn.GetType().GetProperty(itemOut.Name); itemOut.SetValue(tOut, propIn.GetValue(tIn)); } foreach (var itemOut in tOut.GetType().GetFields()) { var fieldIn = tIn.GetType().GetField(itemOut.Name); itemOut.SetValue(tOut, fieldIn.GetValue(tIn)); } return tOut; } }
通過序列化實現
/// <summary> /// 使用第三方序列化反序列化工具 /// 還有automapper /// </summary> public class SerializeMapper { /// <summary> /// 序列化反序列化方式 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> public static TOut Trans<TIn, TOut>(TIn tIn) { return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn)); } }
反射和序列化兩種實現方式性能不太好;
通過表達式目錄樹動態的生成硬編碼
Func<People, PeopleCopy> func = p => new PeopleCopy() { Id = p.Id, Name = p.Name, Age = p.Age }; PeopleCopy peopleCopy3 = func.Invoke(people);
方法一:普通緩存
/// <summary> /// 生成表達式目錄樹 緩存 /// </summary> 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)) { 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<TIn, TOut> func = lambda.Compile();//拼裝是一次性的 _Dic[key] = func; } return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn); } }
方法二:泛型緩存,性能較高
/// <summary> /// 生成表達式目錄樹 泛型緩存 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> 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); } }
感謝各位的閱讀!關于“C#中如何實現表達式目錄樹Expression”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。