1. Main design model of LINQ framework

At this point we can see the principle of LINQ dimly, it is not the hanging garden, it has a foundation. Supported by a series of new features above, Microsoft has made the above features interact coherently and form a natural integrated query framework through a large-scale build extension approach. These features are the language for LINQ enhancements, it can also be said that designers are constantly exploring new language features in line with the modern development system, but also more and more support for functional programming features, such as the DLR introduction of Python, Ruby functional scripting language strong support. Other functional scripting languages will increasingly be supported later. Now we are going to focus on the object model. What is an object model? In fact, most of the time, we pay attention to the language level of learning and do not focus on the design principle of the object, resulting in the increasing cost of learning. It is more important for us to learn and develop design ability (so-called design ability reflects technical level). Object model is simply the object design model, how to construct the deep object structure to meet the needs. At present, the main ORM framework on the.net platform, ADO.NET EntityFramework, embodies the role of object model in the design of the conceptual layer of the architecture system. There are many object models worth exploring in the ADO.NET EntityFrameWork and Linq to SQL framework. LINQ is full of extension methods. Behind these extension methods is a big design secret, which is the chained programming model. We will learn the chained programming model in detail to understand why LINQ can use the same method coherently and appear so elegant.

1.1. Chain design mode (design system logic in pipeline-like link mode)

The chain design pattern is a beautiful pattern that has been neglected for a long time. The last time I saw its beauty was when I was studying LINQ and saw the emergence of a continuous extension method, it made me feel really good. In fact, in many cases, we can also use this design mode for reference, which can naturally deal with a lot of tricky problems. The bold design is to use chain mode to artificially recombine the fragmented business algorithm after business fragmentation. If the design is good, it will be a top feast. Since this article is about LINQ, I won’t cover it here, but there will be another article on bold chained business process reengineering. In many cases, when we design a system function or application framework, we can use the chain design mode to elegant our development mode, so that the coding is very smooth and convenient. In order to vividly express the use of chain design mode, HERE I use a relatively simple small example to show its design concept and use.

Example: Suppose I have an object type representing students and a set of students. The student collection type is mainly used to accommodate the student entity. The collection type provides a series of methods to perform continuous operations on the collection. The most common one is the filtering operation. For example, all female students are screened out, and then the list of students over 20 years old is screened out from all the selected female students, and then the list of students from Nanjing, Jiangsu province is continued to be screened. I think LINQ is the most common approach, because LINQ is built for queries, and queries are mostly for collection data.

Object diagram:



In the Student class, we define several basic Student attributes. The important methods in StudentCollection are SelectByFemale method and SelectByMankind method, which are methods to select students as female and male respectively. SelectByAge and SelectByAddress are used to filter ages and addresses, respectively. Because the specific method code is relatively simple, I will not post here, the purpose is to let you can intuitively see the benefits of the chain design pattern and flexible place.

Sample code:

 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 }; 
 8 // Initialize the StudentCollection with the Student array
 9 StudentCollection StudentCollection = new StudentCollection(StudentArrary); 
10 StudentCollection WhereCollection = 
11     StudentCollection.SelectByFemale().// Select all female students
12     SelectByAge(20).// Select a list of students aged 20
13     SelectByAddress("Nanjing, Jiangsu");// Select * from student list whose address is "Nanjing"
Copy the code

Doesn’t it look elegant? I feel elegant and comfortable. One of the things that’s kind of weird about StudentCollection is that each method has to have a return type that makes the next call work, so it has to be the same data type every time. That’s the StudentCollection collection type.

Most of the time, there is a blind spot in our design thinking. That is, every time we return, it has nothing to do with this time. Chain programming seems to find this blind spot and seriously emphasize to us that we should exercise this design blind spot regularly. We use mind maps to analyze the blind spots of chain design, and also look for design benefits that we often overlook.

Mind mapping:



Each method in the figure above has a return return type, but make sure that the type returned can be the operation object of the next method. When designing object methods, you definitely need to break up large procedures into smaller procedures that can be organized. Most of the time when we design the object model is also difficult to think about this, in fact, we are not skilled enough, we need to do more practice and read more books on design, the rest of the time.

1.2. Chain query method (process each working point in query expression step by step)

In the chain design pattern above, we learned that if we build a loop object model, we can use the object collection repeatedly to perform repeated query operations. In fact, LINQ uses this approach as its query principle. Here I will get right to the core design principles of LINQ. LINQ’s chain model is mainly used in the collection of query objects, which are filled with the corresponding query methods of LINQ expressions that can be used by building extension methods on a large area. So how do we understand LINQ queries? LINQ: from *** in *** where *** select *** This is a new query syntax built on top of CTS, handled by an editor, and not in C# or a managed language like VB.NET. In fact, we all know that C#, VB.NET and other syntax are based on. NET platform IL intermediate languages, they are part of the source code, not the final output of the program. IL is the program code that we output each time we compile. The syntax of LINQ is ultimately the syntax of IL, and when we write LINQ query expressions the editor has already intelligently translated them into object methods. Too many principles are introduced in the next section. On the chain query method is also an object design problem, we see the chain design pattern can be very natural to build in line with our own actual needs of the chain query method, this series of query methods to add a big problem is that can not be dynamically added to the object to expand the internal. For example, the published object is not directly modified, so here we use the extension method technology mentioned above, through the extension method we can easily add behavior for the published object. Let’s look at a little example just to reinforce the impression.

Example: Suppose I have a set of published ORM simple components built on. NET2.0, now I need to expand it into a chain query mode, and do not want to use the previous cumbersome query mode. So I need to build a separate one. NET3.0 or. The NET3.5 extension is an extension of the previous assembly. It can be used or not used, so that we can use extension methods or other new syntactic features.

 1 /// <summary> 
 2         ///Get the Base_Deptment object collection based on the existing properties in the Base_Deptment object.
 3         /// </summary> 
 4         /// <param name="model">Base_Deptment instance</param> 
 5         /// <returns>Set of Base_Deptment object instances</returns> 
 6         [ContextLogHandler(OperationSort = 1, OperationExtension = typeof(Dal.LogWrite))] 
 7         public List<Base_Deptment> GetAllListByPropertyValue(Base_Deptment model8)         { 
 9             return ORMHelper.FindEntityList<Base_Deptment>(model); 
10         }
Copy the code

Ormhelper. FindEntityList is a generic method that queries a list of objects based on existing attributes of an entity. If I needed to add additional conditions I would have to add a value for the Base_Deptment type parameter model to use, now I want to extend it to use chained queries via chained design mode, as in:

 1 /// <summary> 
 2         ///Get the Base_Deptment object collection based on the existing properties in the Base_Deptment object.
 3         /// </summary> 
 4         /// <param name="model">Base_Deptment instance</param> 
 5         /// <returns>Set of Base_Deptment object instances</returns> 
 6         [ContextLogHandler(OperationSort = 1, OperationExtension = typeof(Dal.LogWrite))] 
 7         public List<Base_Deptment> GetAllListByPropertyValue(Base_Deptment model8)         { 
 9             Base_Deptment QueryModel = new Base_Deptment(); 
10             var selectList = QueryModel.Select((Base_Deptment Model) => Model.FiledObject.BDeptCode) 
11             .Where((Model) => Model.FiledObject.BDeptCode == "800103848") 
12             .OrderByAscending((Model) => model.FiledObject.BDeptCreateTime); 
13             return selectList; 
14         }
Copy the code

The code here is only understood in context, and it may make a little bit of sense, but it doesn’t make any difference.

This is how you can design an otherwise bloated feature into such an elegant use. I will cover the Linq to CustomEntity implementation in a later article, so I won’t go into the details here. The example itself is meant to show us that chained queries can be used to implement more user-friendly and elegant components or frameworks.

2. The core design principle of LINQ framework

2.1. Languages on top of hosted languages (LINQ query expressions)

Through the above examples we should basically understand the chain design pattern, chain query method of mystery and use. Using a simple example, we can also see that the chained query method has unique advantages in data query, which is a good way to understand LINQ. So what does the chained query approach prepare for LINQ? Have a corresponding method prepared? Yes, the chain design pattern has made sufficient theoretical foundation for the chain query, and then through the large-scale construction of the chain query method and LINQ query expression query operator corresponding to naturally become a good link to use LINQ query any data source. LINQ provides a unified query interface, and then forms the user’s operation data into a Lambda expression through a custom chained query method, and then sends the data driver to query the data by extracting the relevant data structure of the Lambda expression into your own desired parameters. LINQ itself is not a managed language; it is a convenience syntax supported by the editor to reduce the hassle of using query methods directly. In contrast, the effort and time involved would have been much greater if we had just used the query method.

Sample code:

 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 }; 
 8 
 9 var list = StudentArrary.Where((studentModel) => studentModel.Name == "Wang Qingpei").Select((studentModel) => studentModel); 
10 var list = from i in StudentArrary where i.Name == "Wang Qingpei" select i;
Copy the code

There are two ways to query the collection data. The first is to query the data using a chained query. The second is to query data using LINQ query expressions. There is no doubt that LINQ is convenient, simple and convenient more in line with the SQL query we are used to.

This makes it easy to get a filtered object. The editor handles LINQ, not the CLR, and the editor handles LINQ into the basic set of interfaces implemented by the framework. Remember, LINQ is syntactic sugar, it is not supported by C#, VB.NET or the basic kernel of CLR.

2.2. The foundation of managed language construction (LINQ attaches to the general interface and connects to the corresponding method of query operator)

LINQ is a unified data query interface, so how does it interface directly with different data sources? In 4.1 summary, we can easily query the Student[] array with a simple LINQ query expression. How does this work? Let’s take a step by step look at how LINQ manages unified data queries. Let’s say there’s no LINQ. Let’s see. How.NET builds linQ-enabled inner libraries bit by bit. LINQ was introduced in.net 3.5. The Core assembly, system.core.dll, has two namespaces that are directly related to LINQ. Linq(the set of chained query methods directly corresponding to Linq query Expressions) and System.linq.Expressions(the tree of logical Expressions in Linq query Expressions). The first one in System.linq is the Enumerable static class, which is a static extension method that encapsulates the query IEnumerable interface type. It should be noted that LINQ query data source is mainly divided into two categories, must support is also the first support is LINQ to Object, for the in-memory object query of course is the main IEnumerable object, query is oriented to the collection class, in. NET is the use of IEnumerable as an iterator object interface, so in the System.Linq.Enumerable static class is all encapsulated in the IEnumerable interface chain query method, these methods are provided through the extension method, that is, in. Not available in NET3.5 or later, extension assembly packages will not be loaded. More importantly, all the logical expressions in the extension methods are Func generic delegates, that is, delegates are directly used to perform logical operations. When we call them, the conditions of the logic are given in the form of Lambda, and the logic is compiled directly into anonymous methods that can be executed, instead of the Expression object. For in-memory object queries, call directly. Another type of LINQ query that is supported by our own data sources is provided by the System.linq.Queryable class. Editor will think you are a query from the data source object, will call you to achieve in the implementation of the System. Linq. IQueryableProvider interface implementation class. This class provides parsing and execution of expression trees. Scan System. Linq. Queryable static class all of the extension method and System. Linq. Enumerable extension method in the class of difference is all types of Func System. Linq. Expressions. The Expression type of packaging, It also accord with our last article, on the System.. Linq Expressions. The analytical Expression is as a data structure, in need of our own to read the related logical structure. The syntax of Linq query remains the same whether it is to query Linq to object or self-defined data source. This is to unify the data query interface, but to change the data query provider. Linq to Sql and Linq to Entities all implement the self-defined data source query function.

2.3. IEnumerable, IEnumerable, Enumerable(LINQ to Object framework entry)

LINQ queries have been supported in 4.2, so what is the difference between queries? What is the difference between using IEnumerable and IQueryable? How to understand the relationship between the two in the overall framework of LINQ. LINQ is unified. NET platform data query interface, no matter what type of data we want to query, no matter where the data in the network world, we can be very good to query. Therefore, no matter what kind of data we want to query, we need to create a mature object model, if it is still a DataTable or a DOM tree, it does not make much sense, we need to be able to continuously query objects in memory. When we query the data from the remote server into memory, we need to use the object model we created to objectify it in preparation for Linq to Object. Linq to Custom can be executed perfectly only if Linq to Object is available. This is a reverse relationship. The generic IEnumerable interface inherits from the IEnumerable interface, which represents an iterable collection of data. Linq to Object is a query for the IEnumerable collection. All the static methods in the Enumerable static class correspond to the LINQ query expression that operates on the IEnumerable collection type. You call the static method directly in Enumerable every time you query. There’s not much to say about Linq to Object, but you need to be familiar with Linq’s query expression syntax.

2.4. Access to IQueryable, IQueryable, Queryable(LINQ to Provider framework)

The IQueryable interface is provided to implement custom data sources and is used to support strongly typed data sources. When LINQ is used to query the IQueryable interface, the query expressions are compiled directly into the corresponding static extension methods of the Queryable static classes. A logical condition is treated as a query expression instead of a delegate like the IEnumerable interface. Of course, it is difficult to implement LINQ query data sources by ourselves. We need to deal with the expression tree ourselves, which will be explained in more detail in a later article.

2.5.LINQ query interface for different data sources

So far, I think we have a general understanding of LINQ’s unified data source query. No matter what our data source is, RDMS, DOM, etc., we have corresponding query methods. It is only hard for the encapsulated people, and friends who do background development may need to use these special query languages to query data. Make it easy for front-end programmers to query data sources using LINQ. The primary task of the component developer is to create an object model that is an abstract model of the real data source that can be successfully placed into the IQueryable database for query.

2.6. The framework principle of LINQ is summarized as a whole

Through the above detailed introduction we have a basic grasp of LINQ framework, if just use it is actually very simple, as long as familiar with LINQ query syntax on the line, but I think every programmer has a strong curiosity, want to understand the design principle of the framework, this is also we must have combat effectiveness. LINQ query expressions are the chain query methods that are defined in static classes. IEnumerable is executed using anonymous method invocations, while IQueryable is manually resolved, which is a custom data source. Linq to XML, Linq to SQL, Linq to Entities, and other lightweight query libraries are excellent examples of extended data sources that are worth exploring.