Objective
I believe you are right
Entity Framework
Certainly not unfamiliar, I believe that LINQ to SQL is one of its biggest bright spots, but we have been using until now do not understand how the internal implementation, today we will briefly introduce IQueryable and Iqueryprovider.
IQueryable interface
Let's talk about this interface first, because we often see the return type of a LINQ to SQL statement in the use of EF
IQueryable
, we can look at the structure of this interface:
The code is as follows:
public interface Iqueryable:ienumerable
{
Type ElementType {get;}
Expression expression {get;}
Iqueryprovider Provider {get;}
}
It may be strange to say that when we use this interface in the development process, we provide more than that, because Microsoft provides a powerful
Queryable
Class, of course, you do not think that this class is implemented IQueryable and then implement a lot of methods, if so those third-party library How to customize it? So queryable is just a static class, the IQueryable interface is extended, the following is the author in
. Net Reflector
Part of:
If the reader is careful, you will find that LINQ to SQL does not lead to actual queries, and only start querying data from the database when we actually start using it.
Iqueryprovider interface
If we debug the EF, we'll see the generated T-SQL statement. T-SQL is based on the expression tree analysis, and the core is the Iqueryprovider interface, the following is the structure of the interface:
The code is as follows:
public interface Iqueryprovider
{
IQueryable createquery (expression expression);
Iqueryable<telement> createquery<telement> (expression expression);
Object Execute (expression expression);
TResult execute<tresult> (expression expression);
}
which
CreateQuery
is responsible for parsing the expression tree, of course, but also to return the results of processing, in order to continue to analyze the following statement, of course, this is only analysis, you can completely according to the expression tree to get your own query statements, such as SQL or other, only when the actual use of data will be called
Execute
method, this time we can start the actual query according to our own analysis of the statement.
Example analysis
Queryprovider class
We can never understand the principles, so let's take a simple example to illustrate the following. First we implement the Iqueryprovider interface, which will use a query class, this class will be introduced later, first we create a new
Queryprovider
class implements the Iqueryprovider interface, first we look at the
Createquery<s>
Method:
Here's
Expression
Is the expression tree that is passed to us and needs to be processed, and finally we return an example of implementing the Iqueryable<s> interface so that LINQ can make the following query on this basis, where we just create an instance of query, Pass expression to it at the same time, because here is just a demo, so we don't really parse the expression tree
(That's a lot of work to do)
。 Then there is the CreateQuery method:
We can see the following phrase:
The actual meaning is to create
Query<>
, and the generic parameter is
ElementType
, the parameter is
This
And
Expression
。
The Last is
Execute
method, passing a
Expression
Parameters, and get the final result, the author here directly is the value of the Write dead:
Query class
Only
Queryprovider
No, we also need a class that can save the state of the expression tree, and of course the result of our parsing of the expression can be saved in it, so that we can execute the execution and return the result in the Iqueryprovider Execute method based on the results we parse.
Here we can see the expression value of query when creating this instance, if the expression parameter is not passed when the value is:
However, in the following procedure, expression in query will be the expression value in Queryprovider.
In fact, we have completed a simple example, we can begin to test our results, the author is using the following code to test:
OK, let's start by looking at how this LINQ statement is analyzed.
First we look at the return value of expression in query at the beginning of the execution (for example):
After getting to this expression, we begin to execute LINQ, where item = = 123 is executed first.
Analyze where item = = 123
Then we F5, we can see the queryprovider in the
Createquery<s>
Hit, and
Expression
The parameters are as follows:
We see that the string inside is
Where (item = = 123)
In this sentence we can understand that in fact, where in LINQ is essentially the use of the Where method, and pass it to its corresponding lambda expression. After analyzing the where section, here is
FirstOrDefault
Part of it.
Analysis FirstOrDefault
When executed to
FirstOrDefault
When we can look at the value of T, we find that T is actually the return value of createquery<s> in Queryprovider.
And then we start doing the following
FirstOrDefault
method, the discovery will once again get
Expression
, and the value of expression at this point is the top
Createquery<t>
The arguments passed to us
Expression
。
This expression tree is then represented by an expression tree
FirstOrDefault
The values of the method call are spliced together and the Execute<s> method in Queryprovider is called, and we can see the value of the expression passed to us at this time.
At this point a simple process is over, and the end is to return the author to write dead 123 of this value.
Through the above example we have a basic understanding of the process of its work, below we will be a step-by-step analysis of our
where item = = 123
, of course, we will use recursion, so please tidy up your own ideas, step by step to see how to parse this statement from an expression tree.
Parse an expression tree in combat
First, we have a method of analyzing the expression tree, which we put in
Queryprovider
In
The code is as follows:
public void Analysisexpression (Expression exp)
{
Switch (exp. NodeType)
{
Case Expressiontype.call:
{
Methodcallexpression MCE = exp as methodcallexpression;
Console.WriteLine ("The Method is {0}", MCE.) Method.name);
for (int i = 0; i < MCE. Arguments.count; i++)
{
Analysisexpression (MCE). Arguments[i]);
}
}
Break
Case Expressiontype.quote:
{
Unaryexpression UE = exp as unaryexpression;
Analysisexpression (UE. Operand);
}
Break
Case EXPRESSIONTYPE.LAMBDA:
{
LambdaExpression le = exp as lambdaexpression;
Analysisexpression (le. Body);
}
Break
Case Expressiontype.equal:
{
Binaryexpression be = exp as binaryexpression;
Console.WriteLine ("The Method is {0}", exp. Nodetype.tostring ());
Analysisexpression (BE. left);
Analysisexpression (BE. right);
}
Break
Case Expressiontype.constant:
{
ConstantExpression CE = exp as constantexpression;
Console.WriteLine ("The Value Type is {0}", CE. Value.tostring ());
}
Break
Case Expressiontype.parameter:
{
ParameterExpression PE = exp as parameterexpression;
Console.WriteLine ("The Parameter is {0}", PE. Name);
}
Break
Default
{
Console.Write ("Unknow");
}
Break
}
}
And in
Createquery<s>
Call this method in the
Then we can start to run the test, in order to let the reader understand the current processing of the expression tree, so in the following will contain
Analysisexpression in Parameters Exp
, which makes it easy for the reader to distinguish between the currently processed expression tree.
The NodeType in the Ps:expression type is very important because the parent expression type is passed to us, and we need to convert the NodeType to the corresponding subclass so that we can get more detailed information.
Expressiontype.call
We enter this branch according to the NodeType of the first exp, because where is essentially the SS call where method, so we convert exp to the corresponding
Methodcallexpression
Type so that we can see the method name of the call.
Of course call a method must have parameters, so the following also need to loop
Arguments
To analyze the specific parameters, including the object that calls this method, naturally we first parse the object that calls this method, here we make the first recursive call, jump to expressiontype.constant.
Expressiontype.constant
NodeType for this type, we can go through
ConstantExpression
Type to get the corresponding parameter, through value we can get to the object that called the Where method, of course, here will not continue to analyze the downward.
So let's go ahead and jump to the previous for loop and start analyzing the second parameter, which is the part of item = = 123.
Expressiontype.quote
If a person who has touched a lambda may think that the type should be a lambda, but actually does not jump directly to that, but first jumps to quote, and then we convert to
Unaryexpression
Type, and then continue to analyze where
Operand
property, and the NodeType of this property is lambda. Personally, this should be a way of distinguishing between lambda and common, because where not only can receive lambda but it can also be a regular method, so this layer is needed.
Expressiontype.lambda
Jump to this, people will not feel strange, here for brevity. The author does not analyze the parameters, but directly analyzes
Body
Part, because this part is the key to us.
Expressiontype.equal
We see this lambda is very simple, is an equal comparison, so jump directly to the equal, and of course, and, or and other corresponding enumeration, and to this step we can directly analyze left and right, of course there is a small episode, When I look at the exp type when I jump to this enumeration, it's actually
Logicalbinaryexpression
Type, not
Binaryexpression
Type, and then use the reflector to view the next, I will hehe.
I was also surprised, how did not have this type, finally knew to play is this out. So far, let's continue to analyze the left and right parameters of this equality operation.
The first analysis is the left parameter item.
Expressiontype.parameter
Item is picked up and converted into
ParameterExpression
Type, the author only outputs the name of the parameter here.
To the left of the parameter analysis is complete, we begin to analyze the right parameters.
Expressiontype.constant
We can easily think of the corresponding value is 123, to this entire expression analysis is complete.
Let's look at the output of the final console.
I would also like to state that we should understand the principles of the various libraries we use, so that we will be able to add some features that match the actual development in the future, which is not a waste of time. But to improve the development of the project in the future, with the continuous accumulation, we will find a lot of repetitive features do not need us to repeat the writing, and the time saved we can do what we want to do, so we have to do a lazy programmer thinking.
In addition to the Declaration,
Running GuestArticles are original, reproduced please link to the form of the address of this article
IQueryable and Iqueryprovider in the C # Entity framework
This address: http://www.paobuke.com/develop/c-develop/pbk23160.html
Related content The timing of dynamic add event bindings for controls in C # Seven classic sorting algorithm series (top) A tuple instance of the new C # syntax features how to use regular expressions to judge characters in C #
An example of the use of a singleton pattern in design Patterns in C # programming super cool WPF implementation loading control effects C # Methods for implementing Windows Form copy content to the Clipboard method for drawing rounded rectangles in C #
IQueryable and Iqueryprovider in the C # Entity framework