美文网首页
C#手动拼装Expression,利用"泛型缓存"+"反射"实现

C#手动拼装Expression,利用"泛型缓存"+"反射"实现

作者: 浅谈码生活 | 来源:发表于2020-09-15 10:30 被阅读0次

    首先,我们先认识一下Expression:Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
    Expression的定义为拉姆达表达式,但Expression本身是一种数据结构,就像对一个计算做了一个精确的描述,将其展开后会发现,分为“左边”及“右边”,每个元素都可以将值获取出来,所以本质可以理解为-二叉树。
    1.表达式目录树可以通过compile 转换成一个委托

    Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//表达式目录树只能有一行;
    var expFunc = exp.Compile();
    

    2.手动拼装表达式目录树

    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(); 
    

    3.利用"泛型缓存"+"反射"实现类型硬编码转换

    /// <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);
        }
    }
    

    不太理解的朋友建议先了解泛型缓存机制。
    4.拆分Expression表达式,组合SQL语句:

    //1.准备Expression表达式目录树
    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%';
    
    //2.继承目录树“访问者”抽象类,并对上述表达式包含的“属性”,“方法”,“常量”等方法进行重写:
    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;
        }
    }
    

    相关文章

      网友评论

          本文标题:C#手动拼装Expression,利用"泛型缓存"+"反射"实现

          本文链接:https://www.haomeiwen.com/subject/tvbxyktx.html