1. Introduction

Let me interrupt before I start reading this article. In fact, this article was not intended to include the section of “introduction”, but later I thought it was necessary to give feedback to readers. After the detailed explanation of the first three articles, we basically have a basic understanding of the LINQ framework, including its design model, object model, and so on. We know that LINQ query expression is actually a syntax sugar above C#, but this sugar is really good, very convenient and timely. A series of LINQ support principles are introduced in a wide range of theories, do not know the effect;

At the end of my last post, I saw a senior comment suggesting that I write more about the use of LINQ rather than these theories. By the way, I take this opportunity to explain that THERE are so many LINQ usage articles on the Internet that there is no need or value to write about them. There are so many LINQ usage articles on the Internet, from beginners to complex applications, that programmers of all levels can find articles for them. I think these articles belong to use classes, when used in practical projects slightly can check up line, and is important to understand the principle of which is our long-term pursuit, because these principles in the design of any application framework are interlinked, can help us to extrapolate learning, reduce the learning cost, Constantly improve the internal design ideas.

The so-called design ability reflects the level of technology, it is true. Comrades, our constant pursuit should be design, not just use. When you understand the principle, I think everyone can come up with different application directions, then the development of technology makes sense, of course, this is the most valuable.

2. Extending Linq to Object (application framework with query function)

We know that LINQ supports IEnumerable and IQueryable queries, and that the query capabilities that we want to expand LINQ are also focused on these two aspects. Most of the time when we write the application framework, we will implement the IEnumerble object, generally do not use the collection class provided by the system, so as to make the framework OO, context coherence, more modeling. If the application framework has a certain query capability is not very convenient. For example, if you are developing a data-intensive framework, it may not be real-time persistence, but it can provide some kind of query tool externally to query the data in memory, so we need to extend the Object query capability of LINQ. In this section we will learn how to extend Linq to Object.

LINQ query Object is an IEnumerable Object, not a collection Object. LINQ queries for IEnumerable are supported by the Enumerable static object, which then expresses the logic in an anonymous expression so that the collection of queries takes its course. So how do we extend Linq to Object? You can either extend an IEnumerable object by providing an extension method, but you can’t even imagine VS supporting a keyword that would allow you to use an extension method. Also, inheriting IEnumerable gives our collection types the query power of LINQ. Of course, it depends on our needs. From a technical point of view, only these two points can be expanded at present. If we use an extension method we can only extend the IEnumerable object, which is fine. We can easily call our own extension methods in LINQ expressions and have our methods follow the chain query. If you inherit an IEnumerable object extension, the situation becomes a little more complicated. You must specify the object that you’d like to extend in your method. If the object that you’d like to extend doesn’t adhere to the object that you inherit, you’ll break all your extension methods.

We’ll take a look at the above theory in an example. We’ll start with an IEnumerable extension method.

Code snippet: Order class

/// <summary> 
///Order type
/// </summary> 
public class Order 
{ 
    /// <summary> 
    ///Name of the order
    /// </summary> 
    public string OrderName { get; set; } 
    /// <summary> 
    ///Place the order of time
    /// </summary> 
    public DateTime OrderTime { get; set; } 
    /// <summary> 
    ///The order no.
    /// </summary> 
    public Guid OrderCode { get; set; }}Copy the code

This is an order class purely for demonstration purposes. It has three properties: “OrderName “, “OrderTime”, and “OrderCode”, which we will use to complete the example later.

If we were to use the system-provided IEnumerable object directly, we would simply build an extension method for IEnumerable to extend the collection type. I assume that a List is used to hold information about a batch of orders, but the business logic requires that we support processing of the order set data by providing a separate set of extensions. This set of independent extensions follows the current system deployment and is not part of a common development framework. This is very convenient and flexible, can completely replace part of the hierarchical architecture of the Service layer, BLL layer of the logical code section, looks more elegant.

Think again, we can even do a lot of articles in the extension method, the extension method into the system architecture analysis, using the extension method to encapsulate streamlined processing logic, business fragmentation processing, validation chain processing is very good. Only in this way can we really make this kind of technology deeply rooted in the hearts of the people, in order to use it flexibly in the actual system development.

Let’s build a simple IEnumerable extension that handles whether or not the data in the current collection can be inserted.

Snippet: OrderCollectionExtent Static class

public static class OrderCollectionExtent 
    { 
        public static bool WhereOrderListAdd<T> (this IEnumerable<T> IEnumerable) where T : Order 
        { 
            foreach (var item in IEnumerable) 
            { 
                if(item.OrderCode ! =null&&! String.IsNullOrEmpty(item.OrderName) && item.OrderTime ! =null) 
                { 
                    continue; 
                } 
                return false; 
            } 
            return true; }}Copy the code

OrderCollectionExtent is a simple extension method class that has only one WhereOrderListAdd method, which determines whether the Order objects in the current collection meet the insertion condition. The condition determination is not important, but only meets the needs of the example. This method requires the Order type generic constraint so that the extension method cannot be used by other types.

List<Order> orderlist = new List<Order>() 
            { 
                new Order(){ OrderCode=Guid.NewGuid(), OrderName="Fruit", OrderTime=DateTime.Now}, 
                new Order(){ OrderCode=Guid.NewGuid(), OrderName="Office supplies",OrderTime=DateTime.Now} 
            }; 
            if (orderlist.WhereOrderListAdd()) 
            { 
                // Perform the insert
            }
Copy the code

If.NET supported extended attributes (though Microsoft will certainly do so later), methods would not be used to make similar judgments. In this way, the logic of the previous BLL layer is implemented gracefully, and the extension method of this part can be changed dynamically, and can be built in a separate assembly. By the way, in the current MVVM mode, a lot of interface logic in V can also be encapsulated in the extension method to reduce the coupling degree and complexity in VM. MVC, including today’s MVC, can be appropriately extended to achieve a more convenient usage pattern.

But for the most part we extend it to all IEnunerale types, so that it works well with Linq’s chained programming. The principle is so much, according to the specific project needs appropriate adoption.

I think most of the time we use IEnumerable directly by inheriting IEnumerable interface implementation class, but in writing system components, frameworks are generally to implement their own iterator class. It is convenient that the extension methods can also be applied to our inherited classes, before we realize that our own extension components will also support Linq queries. However, you should properly control your extensions to inherited classes. Extension methods should be designed for your internal use, and should not contaminate external objects.

Let’s take a look at the example that uses IEnumerable as an inheritance.

public class OrderCollection : IEnumerable<Order> 
   { 
       List<Order> orderList; 
       public OrderCollection() 
       { 
           orderList = new List<Order>() { 
               new Order(){ OrderCode=Guid.NewGuid(),OrderName="Order", OrderTime=DateTime.Now}, 
               new Order(){ OrderCode=Guid.NewGuid(),OrderName="The order 2", OrderTime=DateTime.Now}, 
               new Order(){ OrderCode=Guid.NewGuid(),OrderName="The order 3", OrderTime=DateTime.Now} 
           }; 
       } 

       public IEnumerator<Order> GetEnumerator() 
       { 
           foreach (var order in orderList) 
           { 
               yield return order; 
           } 
       } 
       System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
       { 
           foreach (var order in orderList) 
           { 
               yield returnorder; }}}Copy the code

This is an Order collection type OrderCollection class, which is specifically used to store or process Order classes. Whether for compatibility.NET2.0 or other reasons, it is possible to encapsulate the types of collections in. NET2.0 version of the assembly, in. Any version of NET2.0 will provide an extended version of the assembly, so we would have to write our extension method specifically for OrderCollection, otherwise we would contaminate the IEnumerable object.

public static OrderCollection GetOutOrderCollection(this OrderCollection OrderColl) 
       { 
           return OrderColl; 
       }
Copy the code

This time will be very clean use of their own expansion method, will not cause a large area of pollution. Of course, the dependency inversion principle usually has a high level of abstraction and does not directly extend the implementation class. Here is a brief introduction.

This summary will mainly take IEnumerable and its extension methods including Linq query for a complete structural analysis, will give a detailed object structure map.

Object static model, runtime map:



The key part of the figure above is that I ==10 will be encapsulated as an expression and sent directly to the Where method, and the expression behind select will also be sent to the SELECT method [(int I)=> I], which is not drawn here. IEnumerable is the Linq to Object data source. The Enumerable static class is used to extend the Linq query expression, so when we write the Linq query IEnumerable collection, In fact, we are indirectly calling these extension methods, but we do not need to write the tedious Lambda expressions, which the editor generates dynamically for us.

In this section we explain how Linq to Object works. The main principle is that a Lambda expression is passed into the Enumerable extension to form a chain operation. Linq is just a language to help us with quick queries, not. NET or C# part of any. It is available in all languages on the.NET platform. Next we will focus on Linq to Provider so that we can really use Linq at an advanced level.

3… Realize the IQueryable and IQueryProvider interfaces

The focus of this article is to explain the IQueryable and IQueryProvider two interfaces, when we understand the two interfaces, we can be imaginative to implement any data source queries. IQueryable, IQueryProvider two interfaces still have a lot of good things worth our study, which is full of a large number of design patterns, data structure knowledge, we will slowly analyze its beauty.

The IQueryable interface is an entry point to the Linq to Provider. Interestingly, it is not an IQueryable interface that supports a query. When we write Linq statements, we usually write where something and then SELECT something, at least two consecutive extension method mapping calls, but do you know how it’s handled internally? How do they associate a full query whenever A Where is followed by a Select? IQueryable is not an IEnumerable object that can be processed in real time and returned to the next method. So how does it string fragmented execution methods together into a whole, complete query? Now we will analyze the patterns, data structures and framework principles involved one by one. After understanding these, the code is the representation of the model, and it will be understood naturally.

Lazy loading was a technique that predated Linq, but it was largely hidden in the system framework. There are many occasions when we need to build in the lazy loading feature ourselves. Building delay in an IEnumerable object basically uses yield Return to build a state machine that only returns data when iterating. The data is obtained by executing the Provider program in IQueryable, reducing the performance cost of obtaining the data in the first place. The IQueryable inherits from the IEnumerable interface, which is called by the Foreach syntax, but executes the provider’s code in the GetEnumerator method. Let’s examine the code of the IQueryable interface.

public IEnumerator<T> GetEnumerator() 
       { 
           return (Provider.Execute<T>(Expression) as IEnumerable<T>).GetEnumerator(); 
       } 

       System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
       { 
           return (Provider.Execute(Expression) as IEnumerable).GetEnumerator(); 
       }
Copy the code

The IQueryable interface returns the IEnumerator interface type inherited from IEnumerator. Both Linq to Sql and Linq to Entity return strongly-type collection objects, which generally do not query data in real time. . If you want to perform real-time require IQueryable Provider. Direct call the Execute method.

We use the diagram to analyze the principle of lazy loading in Linq to Provider.



This code will not be executed immediately, so let’s trace the execution between the components;



This figure focuses on the continuous operations of the IQueryable object. The idea is that each time an extension method is implemented, a new IQueryable object is created, which contains the expression tree that was executed last time, and so on. Detailed principles are analyzed in the following sections.

Finally, the Orderlist will be an object of type IQueryable that contains the full expression tree and will not trigger data queries unless used. This is the key to lazy loading. If you want to retrieve data can be performed manually in the orderlist immediately orderlist. The Provider. The Execute < TB_Order > (orderlist. Expression) to obtain the data.

3.2】. There is a trap that occurs when source code is analyzed that focuses on the underlying parameters of the extensible methods rather than the objects themselves, and that seemingly different methods are located at different locations. In fact, they come from the same place, where the logical object is the same, but this will just cause us to analyze the bottleneck of the problem, here we focus on the extension method to expand the object.

We directly use the source code to explain it;

public static IQueryable<TResult> Select<TSource.TResult> (this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector) 
{ 
    if (source == null) 
    { 
        throw Error.ArgumentNull("source"); 
    } 
    if (selector == null) 
    { 
        throw Error.ArgumentNull("selector"); 
    } 
    return source.Provider.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TResult) }), new Expression[] { source.Expression, Expression.Quote(selector) })); 
}
Copy the code

This is the source code for the Select extension method of the Queryable class that extends the IQueryable object and operates internally using the Source object, which is a direct reference to the extension object. This is the focus of the issue. Sources that are not familiar with the extended method and chained programming can be difficult to concatenate to IQueryable objects returned by previous methods. Source represents each IQueryable instance, and the object that is currently invoked, no matter how many invocations are made, so the invocations are consistent, much like a bidirectional list. And then the next method will all be a method of this object. So note that this call will follow the last one, not start with a new one. Understanding this is critical for the LINQ analysis that follows.



3.3】. Segmenting implementation of sub-methods at Queryable (extension methods at Queryable)

Linq query is known to be a number of keywords splicing together, line into continuous query semantics, which is behind the principle of the article up and down also said many times, I think it should be roughly understood. In fact, this is a bit like breaking a big problem into many small problems to solve, but not all for the purpose of solving the problem and so on. In chained query, many keywords are common in different query contexts, such as where can be used in the query, can also be used in the update, delete. The issues discussed here may go beyond LINQ, but it makes sense because they share a similar design model.

According to figure 3.2, we already know that objects transferred between extension methods are from different instances but from the same object type, so why perform each keyword operation in segments? Let’s use diagrams to help us analyze the problem.



Both lines of code refer to the Where method, and both require concatenation conditions, but the conditions generated by the Where method do not affect your previous methods. This is where the benefit of piecewise execution comes in: maximum granularity of decoupling for maximum reuse.

3.4】. Design error of chain query method (key: When using iqueryyable, we attempt to analyze source code to help generate expression tree data over the internal use mechanism of iqueryyable, we naturally see the Provider property, the property is the IQueryProvider interface, The annotations clarify that it is the last provider to execute the query, and that the initial instance of the IQueryable is regarded as the entry point to the query, maintains a unique instance throughout the extended method invocations, and finally acquires all expressions, forming an expression tree. But IQueryable plays a joke by invoking CreateQuery, which is returned over a number of times. It looks like a single execution with multiple method calls hidden behind it, creating an execution model that we didn’t even know existed, and making people ecstatic. Let’s take a look at how yable IQueryable is dealt with in the chain approach and see how much it can be hidden.

public static IQueryable<TSource> Where<TSource> (this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) 
{ 
    if (source == null) 
    { 
        throw Error.ArgumentNull("source"); 
    } 
    if (predicate == null) 
    { 
        throw Error.ArgumentNull("predicate"); 
    } 
    return source.Provider.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) })); 
}
Copy the code

Similar to this section of code in the article has appeared above, much the same, we below detailed analysis of its internal principle, in the end is how to build a dynamic is static object model.

This method takes one parameter, which is a conditional expression, and extends the IQueryable interface, which can be used by any derivatives. The return type of the method is also the IQueryable type. The same return type and extension type constitute the minimum loop of chained programming. There are two judgments in the method. The first is to determine whether the code is called by extension methods, preventing us from using extension methods directly, and the second is to determine whether we provided expressions.

So what does the last line of code, which wraps several layers of method calls, really mean? When we break it down in detail, the light suddenly dawns on us.



Due to the complexity of the issue, a comprehensive context analysis of the IQueryable is not provided here. The integrity of this section is ensured. The IQueryable object is an object that each method invocation creates a new instance, which is then automatically accepted and invoked by the next method.

Interface-oriented design seeks separation of responsibilities. Why place the implementation and creation of IQueryProvider over the IQueryProvider? It would be more ingenious if the IQueryable extraction process were to be a separate creation interface, although this is my guess and may be a misunderstanding.