Expression expression tree

1, Understanding Expression

1. In the last article, we talked about Entrust (forget to take a look at it, like it and form a habit). Today's expression has a little relationship with delegation (no direct relationship, just want you to see my other articles). Expression is prepared by. NET for Linq to Sql, and its named space is System.Linq.Expressions

2. I don't know if you have the data access layer framework of user ORM (object mapping entity). I believe that the following pseudo code will not be unfamiliar to you. What we passed in here is expression < func < tSource, bool > > predicate

3. When we go to Expression, we can see that there are some methods in Expression < func < tSource, bool > > and finally inherit LambdaExpression

4. We continue to enter LambdaExpression, and we see some properties (these are the methods and properties of lambda), but finally we see that we inherit Expression

 

5. Continue to work hard to enter Expression. Here we see the final base class. There are many methods in it. We can't finish what we have to say in these two days. Let's briefly introduce some common

 

 

II. Step by step

1. You may see that there is a little bit of confusion above. Don't worry. Let's continue. Let's look at the actual operation below. We can see that we create an Expression and a delegate. We can use the Compile method to convert Expression into a delegate. At last, the result of our execution is the same. (do you think Expression is similar to a delegation? Ha ha, the answer is definitely not)

{
                //Here we see that it's similar to entrustment, but it's not really entrustment
                Expression<Func<int, int>> expression = x => x + 10;
                //Compile Method can Expression Convert to delegation
                Func<int, int> func = expression.Compile();
                //Direct statement delegation
                Func<int, int> func1 = x => x + 10;
                Console.WriteLine("Delegation after conversion--" + func.Invoke(5));
                Console.WriteLine("Entrust--" + func1.Invoke(5));
            }

 

2. Next, we will further analyze that we directly use lambda Expression to create Expression < func < int, int, int > > Expression = (m, n) = > m * n + 3; then we use the underlying code to implement this code, and we can also see clearly that here we are breaking it up step by step, using some objects created in Expression

 //Now let's create a Expression<Func<int, int, int>>

                //Create a m Parameter here the parameter is a value( m,n)Yes, if you have several parameters, create several
                ParameterExpression parameter = Expression.Parameter(typeof(int), "m");

                //Create a n parameter
                ParameterExpression parameter1 = Expression.Parameter(typeof(int), "n");

                //Create a constant 3
                ConstantExpression constant = Expression.Constant(3, typeof(int));

                //First work out the leftmost m*n Result
                BinaryExpression binaryExpression = Expression.Multiply(parameter, parameter1);

                //Then figure out.(m*n)+3 Result
                binaryExpression = Expression.Add(binaryExpression, constant);

                //Splice the above decomposition steps into lambda
                Expression<Func<int, int, int>> expression1 = Expression.Lambda<Func<int, int, int>>(binaryExpression, new ParameterExpression[]
                {
                    parameter,
                    parameter1
                });
                Console.WriteLine("lambda Expression method--" + expression.Compile()(5, 6));
                Console.WriteLine("Self written assembly" + expression1.Compile()(5, 6));

3. If you think it's not enough, let's write some examples expression < func < student, bool > > expression = x = > x.id.equals (15)

 //First of all, define a x Parameters for the above x Parameters
                ParameterExpression parameter = Expression.Parameter(typeof(Student), "x");
                //First of all, we need to split from the left to get the properties
                MemberExpression property = Expression.Property(parameter, typeof(Student).GetProperty("ID"));
                //Get our way
                MethodInfo equals = typeof(Student).GetMethod("Equals");
                //Define our constants
                ConstantExpression constant = Expression.Constant("15", typeof(string));
                //Define a method splicing, the first parameter is our property, the second parameter is the method used, and the third parameter is the parameter passed in the method
                MethodCallExpression coll = Expression.Call(property, equals, new Expression[] { constant });
                //After all the data are parsed, we need to assemble the parameters and methods
                Expression<Func<Student, bool>> expression1 = Expression.Lambda<Func<Student, bool>>(coll, new ParameterExpression[] {
                parameter
                });
                Student student = new Student
                {
                    ID = 15
                };
                Console.WriteLine("lambda Expression method--" + expression.Compile()(student));
                Console.WriteLine("Self assembly method--" + expression1.Compile()(student));

4. We can see that Expression is the continuous disassembly under the figure, and then the assembly and lambda execution

 

3, Getting better

I remember writing before AutoMapper I want to write it to you once. This time, I will satisfy you. When we write Mode and Entity conversion, we will directly write hard code when the amount is small

Student student = new Student
                {
                    ID = 15,
                    Name = "Product Baba",
                    Age = 18
                };
                //Hard encoding
                {
                    //Hard code conversion
                    StudentModel studentModel = new StudentModel
                    {
                        ID = student.ID,
                        Name = student.Name,
                        Age = student.Age
                    };
                }

2. But after we use it too often in our project, we will use AutoMapper to map automatically. Today, we will not use it. We decide to build our own wheels. We will use (1, reflection method, 2, expression tree + dictionary, 3, expression tree + generic delegation) respectively

StudentModel studentModel = new StudentModel();
                    Type type1 = student.GetType();
                    Type type2 = studentModel.GetType();
                    foreach (var item in type2.GetProperties())
                    {
                        //Judge whether it exists or not
                        if (type1.GetProperty(item.Name) != null)
                        {
                            item.SetValue(studentModel, type1.GetProperty(item.Name).GetValue(student));
                        }
                    }
    /// <summary>
    /// Dictionary method promotion
    /// </summary>
    public class DictionariesExpand<T, TOut>
    {
        /// <summary>
        /// Create a static container to hold delegates
        /// </summary>
        private static Dictionary<string, Func<T, TOut>> pairs = new Dictionary<string, Func<T, TOut>>();

        /// <summary>
        /// Transformation object
        /// </summary>
        /// <typeparam name="T">Input object</typeparam>
        /// <param name="obj">input parameter</param>
        /// <returns></returns>
        public static TOut ToObj(T obj)
        {
            //generate
            string key = typeof(T).FullName + typeof(TOut).FullName;
            if (!pairs.ContainsKey(key))
            {
                //First of all, let's create a parameter
                ParameterExpression parameter = Expression.Parameter(typeof(T));
                //Get the type to convert
                Type type = typeof(TOut);
                //Create a container to hold the resolved members
                List<MemberBinding> list = new List<MemberBinding>();
                //traversal attributes
                foreach (var item in type.GetProperties())
                {
                    //Getting parameters item.Name Corresponding name
                    MemberExpression memberExpression = Expression.Property(parameter, typeof(T).GetProperty(item.Name));
                    //Judge whether it exists
                    if (memberExpression != null)
                    {
                        MemberBinding member = Expression.Bind(item, memberExpression);
                        list.Add(member);
                    }
                }
                //Ergodic field
                foreach (var item in type.GetFields())
                {
                    //Getting parameters item.Name Corresponding name
                    MemberExpression memberExpression = Expression.Field(parameter, typeof(T).GetField(item.Name));
                    //Judge whether it exists
                    if (memberExpression != null)
                    {
                        MemberBinding member = Expression.Bind(item, memberExpression);
                        list.Add(member);
                    }
                }
                //Initializes the converted type and makes an initialization assignment
                MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list);
                //All the preparations have been completed and are ready to be generated lambda
                Expression<Func<T, TOut>> expression = Expression.Lambda<Func<T, TOut>>(memberInit, new ParameterExpression[] {
                parameter
                });
                Func<T, TOut> entrust = expression.Compile();
                //Generate the delegation and store it in our dictionary
                pairs.Add(key, entrust);
                return entrust.Invoke(obj);
            }
            return pairs[key].Invoke(obj);
        }
    }
/// <summary>
    /// Generalization of generic methods
    /// When we use static methods, we will execute static parameterless constructors, and we will not call parameterless constructors
    /// When we use generics, we will save copies of different generics, which will not be released in memory all the time, so we can
    /// Implement pseudo hard coding
    /// </summary>
    public class GenericityExpand<T, TOut>
    {
        private static Func<T, TOut> _Func = null;
        static GenericityExpand()
        {
            //First of all, let's create a parameter
            ParameterExpression parameter = Expression.Parameter(typeof(T));
            //Get the type to convert
            Type type = typeof(TOut);
            //Create a container to hold the resolved members
            List<MemberBinding> list = new List<MemberBinding>();
            //traversal attributes
            foreach (var item in type.GetProperties())
            {
                //Getting parameters item.Name Corresponding name
                MemberExpression memberExpression = Expression.Property(parameter, typeof(T).GetProperty(item.Name));
                //Judge whether it exists
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //Ergodic field
            foreach (var item in type.GetFields())
            {
                //Getting parameters item.Name Corresponding name
                MemberExpression memberExpression = Expression.Field(parameter, typeof(T).GetField(item.Name));
                //Judge whether it exists
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //Initializes the converted type and makes an initialization assignment
            MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list);
            //All the preparations have been completed and are ready to be generated lambda
            Expression<Func<T, TOut>> expression = Expression.Lambda<Func<T, TOut>>(memberInit, new ParameterExpression[] {
                parameter
                });
            //Generate delegates and store them in our generic delegates
            _Func = expression.Compile();
        }

        public static TOut ToObj(T obj)
        {
            return _Func(obj);
        }
    }

3. I tested the above code for millions of times

1. Direct hard coding: fastest (0.126s)
2. Traversal attribute form by reflection (6.328s)
3. Use the form of serialization and deserialization: the serialization string of the copied entity is used to deserialize the assigned entity (7.768s)
4. Dictionary cache + expression tree (just understand the splicing code of Lambda) (2.134s)
5. Generic cache + expression tree (just understand the splicing code of Lambda) (0.663s)

Four, summary

1. There are some other uses that I haven't fully introduced. For example, you can encapsulate your own orm. The ORM we use is encapsulated through this. It's better to teach people to fish than to teach people to fish. In the last example, we use a lot of detailed knowledge points.

Tags: ASP.NET Lambda SQL encoding Attribute

Posted on Tue, 17 Mar 2020 00:48:42 -0700 by Ashh RA