1. Dynamic LINQ query (dynamically build Expression tree)

What is a dynamic LINQ query? LINQ is written statically, because C# is designed based on the principle of a static type system, where the type is determined at writing time, meaning that at compile time you know what queries are going to be executed, what the criteria are, how they are sorted, and so on. In a large number of applications, we need to query data sources according to the user’s choice. In the past, we used to concatenate the SQL string of the query through judgment, but now we are faced with strong LINQ query, whether it can be very convenient to conduct similar queries. In fact, there is no mystery, the basic implementation principle is to dynamically build expression tree to implement the IQueryable interface query. The key to dynamic LINQ query execution is that Expression objects can be dynamically compiled into executable delegate objects. Delegate objects are executable code segments that can be used directly. This provides the basis for dynamic LINQ queries. Any query Expression method that uses the IEnumerable type knows that it never accepts an Expression object directly. Would dynamic LINQ work in the IEnumerable interface? The IQueryable extension method object Queryable, that is, the AsQueryable method, which returns an EnumerableQuery object that realizes the IQueryable interface. The implementation content of the object is not very complex. Compile the dynamically concatenated data structure Expression object into an anonymous function that can be executed, and then execute the query directly. To see the point of the EnumerableQuery object, there must be a place where the Expression object is Compiler.

 1 private IEnumerator<T> GetEnumerator(2)         {
 3             if (this.enumerable == null)
 4             {
 5                 EnumerableRewriter rewriter = new EnumerableRewriter();
 6                 Expression<Func<IEnumerable<T>>> expression2 =
 7                     Expression.Lambda<Func<IEnumerable<T>>>(rewriter.Visit(this.expression), (IEnumerable<ParameterExpression>)null);
 8                 this.enumerable = expression2.Compile()(); / / (1) key
 9             }
10             return this.enumerable.GetEnumerator();
11         }
Copy the code

In the “(1) key” areas of the code above, it is clear that the expression tree is dynamically compiled and then subsequently executed, which explains why an IEnumerable object needs to be able to be converted to an IQueryable object. This eliminates dynamic query bottlenecks between the IEnumerable and IQueryable interfaces.

The problem with dynamic LINQ queries is that we can no longer write Lambda expressions at run time. We know that Lambda expressions are compiled into Expression tree objects, so we can build Expression objects dynamically at run time. This allows the dynamically constructed expression tree object to be passed directly into the desired method. The query is dynamically compiled into an executable delegate that can be executed if the query is an IEnumerable, and is resolved and executed by the provider if the query is an IQueryable.

Let’s look at a simple example of a dynamic query:

1  // Construct the Student array
2             Student[] StudentArrary = new Student[3]
3             {
4                 new Student(){Name="Wang Qingpei", Age=24, Sex="Male", Address="Nanjing, Jiangsu"},
5                 new Student(){Name="Chen Yuhe", Age=23, Sex="Female", Address="Yancheng, Jiangsu province"},
6                 new Student(){Name="Jinyuan", Age=22, Sex="Female", Address="Huai 'an, Jiangsu"}
7             };
Copy the code

This is a set of data, so you don’t have to bother with the Linq to Sql data source for simple testing. We will use the query logic by dynamically building the expression tree. Our Lambda will not be useful at this point, and we will not be able to build the delegate type at run time.

The requirement now is to accept a Name value from the interface, so LINQ queries need to be written directly.

1 var list = from i in StudentArrary where i.Name == "Wang Qingpei" select i; 
Copy the code

But we need to build the Expression tree dynamically to execute the query. Each node of the Expression tree has a corresponding Expression derived type, so we just need to assemble the related types. Since the sample program I built is of a console type, let’s take a quick look at how to build an expression tree.

 1 ParameterExpression parameter = Expression.Parameter(typeof(Student), "stu");// Represents the left argument name of the binary operator
 2             // Represents the Name attribute in "stu.Name" of the "stu" parameter. The Name attribute must be reflected metadata so that the framework can find it
 3             MemberExpression property = Expression.MakeMemberAccess(parameter, typeof(Student).GetMember("Name") [0]);
 4             // represents a constant value
 5             Console.WriteLine("Please enter the name of the person to be queried:");
 6             ConstantExpression name = Expression.Constant(Console.ReadLine());// Read the value from the user input stream
 7             BinaryExpression binary = Expression.MakeBinary(ExpressionType.Equal, property, name);// Concatenate the left and right sides of the == operator
 8             // The full expression is Lambda
 9             LambdaExpression lambda = Expression.Lambda(binary, parameter);
10             // The important thing is that we turn the full Lambda expression directly into a delegate that can be executed
11             Func<Student, bool> wheredelegate = lambda.Compile() as Func<Student, bool>;
12             // Execute the compiled executable delegate directly into the Where method
13             var list2 = StudentArrary.AsQueryable<Student>().Where(wheredelegate);
14             foreach (var i in list2)
15             {
16                 Console.WriteLine("Query List:");
17                 Console.WriteLine(Name :{0}, age :{1}, address :{2}", i.Name, i.Age, i.Address);
18             }
19             Console.ReadLine();
Copy the code

Legend:



The focus of this example is on how to build logic dynamically, and it is perfectly possible to encapsulate similar functionality for future reuse depending on project requirements. If you find it troublesome to write the expression tree manually, it is recommended to find an auxiliary class that can print out the object tree of the Lambda expression, and then write against the tree is much easier.

There are not many third-party APIS for Dynamic LINQ. The most commonly used API is dynamic.cs. I have never used it before. Its internal principle is actually the dynamic construction of the expression tree, but this part of the work is done by others, and we are much easier to use.

2.DLR dynamic language runtime (dynamic language runtime based on CLR)

Since C#1, it has become more and more powerful, and the.net platform has become omnipotent. A lot of people are still biting. NET can’t be cross-platform, can’t support dynamic objects, can’t support unmanaged and so on, but what they don’t know is this. .net has quietly made a move to embed a dynamic language runtime on top of a static language runtime. I don’t think it’s that Microsoft can’t support the so-called shortcomings, but it does have its own intentions.

The dynamic language runtime is in. The clR-based runtime environment introduced in NET4.0 is designed to incorporate the advantages of the dynamic runtime in static languages, such as powerful type switching, which is especially important when designing application development frameworks. Any good feature requires a wide range of usage patterns to make it perfect.

When it comes to dynamic runtime, we have to mention the exciting object features defined by VAR in JS. It’s hard to see the advantages and disadvantages between dynamic runtime and static languages without noticing the annoyance that exists when designing frameworks. An obvious example is when we define an object of a datatype and cannot use it in any other way at runtime. Here’s a simple example:

1 dynamic obj = 1;/ / plastic
2 obj = "1";/ / string
3 obj = new { Name = "Wang Qingpei", Age = 24, Address = "Jiangsu" };// Anonymous object type
Copy the code

At runtime we can design the types of objects we want. I dare to assume that it is possible to design ai systems using dynamic runtime features, provide basic prototypes, and then build arbitrary object trees based on the user’s own way of thinking. Technical research is a good place to go, but enterprise applications are likely to be debated.

In the past, it was difficult to dynamically add properties, behaviors, and events to objects at runtime. With the dynamic language runtime, we can easily add what we want.

Let’s take a look at a simple example of dynamically building an object type at runtime. Previously we only used dynamic compilation and CodeDom techniques to do this, but this will be much easier.

 1 static void Main(string[] args2)         {
 3             dynamic objModel = new ExpandoObject();// Initializes ExpandoObject objects that can dynamically add properties, methods, and events
 4             objModel.Name = "Wang Qingpei";// Set the property value
 5             objModel.Age = 24;
 6             objModel.WriteEvent = null;// Hold the delegate field definition of the event
 7             objModel.WriteEvent += new Action<string>(WriteName);// Set the event method
 8             objModel.WriteEvent(objModel.Name + objModel.Age);
 9             Console.ReadLine();
10         }
11         public static void WriteName(string info12)         {
13             Console.WriteLine(info);
14         }
Copy the code

A very simple example tells us that we can write in C# such as JS in the dynamic object function, but is not very mature, dynamic object members have no intelligent tips, should be has not been used in a large area, will certainly be a big meal;

Summary: Now that we’ve covered the basics of the LINQ framework, we’ll learn how to make LINQ query our own custom data sources. Many people like to write their own ORM frameworks, so you can’t help but support LINQ. We will explain in detail how to extend the IQueryable and IQueryableProvider two heavyweight interfaces that allow us to communicate with LINQ, and these two interfaces are very mysterious.