Let's talk about IQueryable & lt; T & gt; and unveil the mystery of the Expression Tree.

Source: Internet
Author: User

[Switch] Let's Talk About IQueryable <T> again, unveil the mystery of the Expression Tree and the secret of iqueryable.

[Switch] Let's Talk About IQueryable <T> to unveil the mystery of the Expression Tree.

Next, let's talk about IEnumerable. Do you really understand the foreach we use every day?

Recently, it was a breeze to customize your own orm in the garden. I don't feel embarrassed to continue mixing my own orm in the blog Garden (joke ). Before that, we need to carefully understand IQueryable <T>, so we have this article.

What is a tree?

What is a tree? This problem seems a bit idiotic. A tree is not a tree. Figure:

From the bottom of the trunk, we can see that the master branch-branch... can be said to be an infinite branch. We can see that:

The most commonly used tree data is XML, and subnodes can be added infinitely under the node. Let's think about the tree structure data that we usually use, such as the unlimited menu classification and the floor of the comment area.

This has nothing to do with what we are talking about today .... Today, we mainly analyze the expression tree. ,

Differences between lambda expressions and Expression Tree:

Lambda expressions:

Func <Student, bool> func = t => t. Name = "Agricultural Code life ";

Expression Tree:

Expression <Func <Student, bool> expression = t => t. Name = "farm Code life ";

At first glance, there is no difference. The Expression is wrapped in Expression. You are wrong. This is only a blind eye for Microsoft to show us. Let's look at the compiled C # code:

The first lambda expression is compiledAnonymous FunctionsThe second Expression Tree is compiled into a bunch of things we don't know, which is much more complicated than the lambda we wrote.

Conclusion:

  • The expression tree we usually use is the compiled lambda expression and then compiled into the expression tree. That is to say, the expression tree we usually use is completed by the compiler. (Of course, we can manually create an Expression Tree. It's just too troublesome. It's not necessary. No one is willing to work hard)

Let's take a look at the magic of the Expression Tree:

Are you sure you want to see it? There are Right, Left, Right, and Left in the Body. Their types are inherited from expressions. The data structure of such nodes that can be appended infinitely is called tree data. That is, our Expression Tree.

Supplement: Student object class above:

public class Student{    public string Name { get; set; }    public int Age { get; set; }    public string Address { get; set; }    public string Sex { get; set; }}
View Code parsing Expression Tree

The above shows the so-called Expression Tree, and others are not as complicated as you think. It is not a tree structure data. If we want to implement our own orm, we will inevitably need to parse the Expression Tree. Recursive Algorithms are usually used to parse tree data. Next we start to parse the Expression Tree.

First define the resolution method:

// Parse public static class AnalysisExpression {public static void VisitExpression (Expression expression) {switch (expression. nodeType) {case ExpressionType. call: // execution method MethodCallExpression method = expression as MethodCallExpression; Console. writeLine ("method name:" + method. method. name); for (int I = 0; I <method. arguments. count; I ++) VisitExpression (method. arguments [I]); break; case ExpressionType. lambda: // lambda expression LambdaExpression lambda = expression as LambdaExpression; VisitExpression (lambda. body); break; case ExpressionType. equal: // Equal comparison case ExpressionType. andAlso: // and Condition Calculation BinaryExpression binary = expression as BinaryExpression; Console. writeLine ("OPERATOR:" + expression. nodeType. toString (); VisitExpression (binary. left); VisitExpression (binary. right); break; case ExpressionType. constant: // constant value ConstantExpression Constant = expression as ConstantExpression; Console. writeLine ("constant Value:" + constant. value. toString (); break; case ExpressionType. memberAccess: MemberExpression Member = expression as MemberExpression; Console. writeLine ("field name: {0}, type: {1}", Member. member. name, Member. type. toString (); break; default: Console. write ("UnKnow"); break ;}}}

Call the resolution method:

Expression <Func <Student, bool> expression = t => t. name = "farm Code life" & t. sex = "male"; AnalysisExpression. visitExpression (expression );

Let's take a look at the execution process:

Layer by layer recursion to subnodes until all nodes are traversed. The final result is as follows:

Basically, we get all the elements and values we want, and then how to assemble them depends on your own mood. Whether it is an SQL statement or a url, please feel free!

Implement your own IQueryable <T> and IQueryProvider

Just parse the expression tree, you can play with your own orm? No. At least it should be encoded Based on the IQueryable <T> interface.

Next we will customize a class MyQueryable <T> inherited interface IQueryable <T>:

 public class MyQueryable<T> : IQueryable<T> {     public IEnumerator<T> GetEnumerator()     {         throw new NotImplementedException();     }     IEnumerator IEnumerable.GetEnumerator()     {         throw new NotImplementedException();     }     public Type ElementType     {         get { throw new NotImplementedException(); }     }     public Expression Expression     {         get { throw new NotImplementedException(); }     }     public IQueryProvider Provider     {         get { throw new NotImplementedException(); }     } }

We can see that there is an interface property IQueryProvider, which is very useful, the main function is to re-create IQueryable when executing the query operator and execute the SQL remote value during the final traversal. We also see the Expression attribute.

Now we understand the relationship between IQueryable <T> and Expression (Expression Tree:

  • IQueryable <T> is mainly used to store Expression (Expression Tree)

Below we also customize the IQueryProvider Interface Class MyQueryProvider:

public class MyQueryProvider : IQueryProvider{    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)    {        throw new NotImplementedException();    }    public IQueryable CreateQuery(Expression expression)    {        throw new NotImplementedException();    }    public TResult Execute<TResult>(Expression expression)    {        throw new NotImplementedException();    }    public object Execute(Expression expression)    {        throw new NotImplementedException();    }}

The above are all automatically generated pseudo code. We will fill in the specific implementation below:

    public class MyQueryProvider : IQueryProvider    {        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)        {            return new MyQueryable<TElement>(expression);        }        public IQueryable CreateQuery(Expression expression)        {            throw new NotImplementedException();        }        public TResult Execute<TResult>(Expression expression)        {            return default(TResult);        }        public object Execute(Expression expression)        {            return new List<object>();        }     }      public class MyQueryable<T> : IQueryable<T>    {        public MyQueryable()        {            _provider = new MyQueryProvider();            _expression = Expression.Constant(this);        }        public MyQueryable(Expression expression)        {            _provider = new MyQueryProvider();            _expression = expression;        }        public Type ElementType        {            get { return typeof(T); }        }        private Expression _expression;        public Expression Expression        {            get { return _expression; }        }        private IQueryProvider _provider;        public IQueryProvider Provider        {            get { return _provider; }        }        public IEnumerator GetEnumerator()        {            return (Provider.Execute(Expression) as IEnumerable).GetEnumerator();        }        IEnumerator<T> IEnumerable<T>.GetEnumerator()        {            var result = _provider.Execute<List<T>>(_expression);            if (result == null)                yield break;            foreach (var item in result)            {                yield return item;            }        }    }
View Code

Run the Code:

Var aa = new MyQueryable <Student> (); var bb = aa. where (t => t. name = "farm Code life"); var cc = bb. where (t => t. sex = "male"); var dd = cc. asEnumerable (); var ee = cc. toList ();

Next let's take a look at the execution process:

Conclusion:

  • IQueryProvider creates a new IQueryable for us each time the Where query operator is executed. <T>
  • When the AsEnumerable () method is called, the actual value is not obtained (only an IEnumerable is obtained). [Note: query in EFNoTake IEnumerable first and then filter it out, because AsEnumerable () will generate an SQL statement to query the entire table]
  • The value of GetEnumerator () is actually called only when the ToList () method is executed.
  • When the value is true, the Execute method in IQueryProvider is executed. (That is, when this method is called, the number of expressions is parsed, and then the result is obtained)

We can see that we should Execute the actual task, but let him return the default value.

It is estimated that some people are upset now. You have to Execute the specific implementation. Okay! (In fact, through the parsing Expression Tree mentioned above, you can do anything you want here .)

First, we use a set as the data source for simplicity:

// Construct the Student array public static List <Student> StudentArrary = new List <Student> () {new Student () {Name = "Nong Code life", Age = 26, sex = "male", Address = "Changsha"}, new Student () {Name = "James", Age = 23, Sex = "male ", address = "Yueyang"}, new Student () {Name = "hi-sister", Age = 25, Sex = "female", Address = "Sichuan "}};

Then, re-write a VisitExpression2 method: (the difference is that the purpose is to retrieve the expressions in the Expression Tree, instead of reassembling them into SQL or something else)

Public static void VisitExpression2 (Expression expression, ref List <LambdaExpression> lambdaOut) {if (lambdaOut = null) lambdaOut = new List <LambdaExpression> (); switch (Expression. nodeType) {case ExpressionType. call: // execution method MethodCallExpression method = expression as MethodCallExpression; Console. writeLine ("method name:" + method. method. name); for (int I = 0; I <method. arguments. count; I ++) VisitExpression2 (method. arguments [I], ref lambdaOut); break; case ExpressionType. lambda: // lambda expression LambdaExpression lambda = expression as LambdaExpression; lambdaOut. add (lambda); VisitExpression2 (lambda. body, ref lambdaOut); break; case ExpressionType. equal: // Equal comparison case ExpressionType. andAlso: // and Condition Calculation BinaryExpression binary = expression as BinaryExpression; Console. writeLine ("OPERATOR:" + expression. nodeType. toString (); VisitExpression2 (binary. left, ref lambdaOut); VisitExpression2 (binary. right, ref lambdaOut); break; case ExpressionType. constant: // constant value ConstantExpression Constant = expression as ConstantExpression; Console. writeLine ("constant Value:" + constant. value. toString (); break; case ExpressionType. memberAccess: MemberExpression Member = expression as MemberExpression; Console. writeLine ("field name: {0}, type: {1}", Member. member. name, Member. type. toString (); break; case ExpressionType. quote: UnaryExpression Unary = expression as UnaryExpression; VisitExpression2 (Unary. operand, ref lambdaOut); break; default: Console. write ("UnKnow"); break ;}}

Then re-implement the Execute method:

Public TResult Execute <TResult> (Expression expression) {List <LambdaExpression> lambda = null; AnalysisExpression. visitExpression2 (expression, ref lambda); // obtain the expression IEnumerable <Student> enumerable = null; for (int I = 0; I <lambda. count; I ++) {// convert LambdaExpression to Expression <Func <Student, bool> type // convert the method Compile () to the delegate method Func <Student, bool> func = (lambda [I] as Expression <Func <Student, bool>) . Compile (); if (enumerable = null) enumerable = Program. studentArrary. where (func); // obtain IEnumerable else enumerable = enumerable. where (func);} dynamic obj = enumerable. toList (); // (Note: You can resolve the SQL statement to execute database queries, or generate a url and request for data .) Return (TResult) obj ;}

Execution Process:

My personal understanding of IQueryable delayed loading:

  • The query operator in the previous section only splits the logic into the Expression Tree and does not remotely execute the SQL statement.
  • Foreache executes IEnumerable <T>. However, IEnumerable <T> also has the feature of delayed loading. Data is actually retrieved during each iteration. When the navigation attribute is used, the database is queried again. (Do not forget the credit of IEnumerable for delayed Loading next time !)

TIPS:

Convert the expression tree to a Lambda expression:

Expression <Func <Student, bool> expression = t => t. Name = "native code life"; Func <Student, bool> func = expression. Compile ();
Summary:

The analysis of the Expression Tree has come to an end. There are still many details or important issues that have not been analyzed. Next time, I will summarize my new experiences.

The Expression Tree first splits the expression into a tree structure (generally, the compiler completes the process), and then assembles it into any desired form based on different data sources or interfaces, this also makes it possible for us to implement our own orm.

Today, we mainly record and summarize the parsing of Expression Tree and the implementation of IQueryable <T> and IQueryProvider. There may be incorrect conclusions or statements, so let's take a look!

Demo download: http://pan.baidu.com/s/1nvAksgL

This article synchronizes data to the index directory: C # basic knowledge Consolidation

 

Recommended reading:

Http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html

Http://www.cnblogs.com/jesse2013/p/expressiontree-part2.html

Http://www.cnblogs.com/jesse2013/p/expressiontree-Linq-to-cnblogs.html

Recommended pig on yuanyou @ tuyere:

Http://www.cnblogs.com/Ninputer/archive/2009/09/08/expression_tree3.html
Http://blog.zhaojie.me/2009/03/expression-cache-1.html

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.