程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# Entity Framework中的IQueryable和IQueryProvider詳解

C# Entity Framework中的IQueryable和IQueryProvider詳解

編輯:C#入門知識

C# Entity Framework中的IQueryable和IQueryProvider詳解。本站提示廣大學習愛好者:(C# Entity Framework中的IQueryable和IQueryProvider詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是C# Entity Framework中的IQueryable和IQueryProvider詳解正文


媒介

      信任年夜家對Entity Framework必定不生疏,我信任個中Linq To Sql是其最年夜的亮點之一,然則我們一向應用到如今卻不曾明確外部是若何完成的,明天我們就簡略的引見IQueryable和IQueryProvider。

 IQueryable接口

      我們先聊聊這個接口,由於我們在應用EF中常常看到linq to sql語句的前往類型是IQueryable,我們可以看下這個接口的構造:


public interface IQueryable : IEnumerable
{
      Type ElementType { get; }
      Expression Expression { get; }
      IQueryProvider Provider { get; }
}
 

      也許會有人很奇異,當我們在開辟進程中應用這個接口的時刻,供給的辦法遠遠不止這麼點,由於微軟供給了壯大的Queryable類,固然年夜家不要認為這個類是完成IQueryable然後完成了許多辦法,假如是那樣那些第三方庫怎樣自界說呢?所以Queryable只是一個靜態類,對IQueryable接口停止了擴大,上面是筆者在.Net Reflector截圖中一部門:

       假如讀者仔細一點會發明linq to sql其實不會招致現實的查詢,只要當我們真正開端應用的時刻才從數據庫中開端查詢數據。

IQueryProvider接口

      假如我們調試的EF的話,會看到生成的T-SQL語句。T-SQL就是依據表達式樹剖析從而得出的,而焦點就是IQueryProvider接口,上面就是該接口的構造:


public interface IQueryProvider
{
        IQueryable CreateQuery(Expression expression);
        IQueryable<TElement> CreateQuery<TElement>(Expression expression);
        object Execute(Expression expression);
        TResult Execute<TResult>(Expression expression);
}
 

      個中CreateQuery就是擔任解析表達式樹的,固然還要將處置後的成果前往,以便接著剖析上面的語句,固然這中央只是剖析,你完整可以依據表達式樹得出你本身須要的查詢語句,好比SQL或許其他甚麼,只要在真正應用數據的時刻才會挪用Execute辦法,這個時刻便可以依據我們本身剖析的語句開端停止現實的查詢了。

 實例剖析

QueryProvider類

      言而不行我們永久不克不及明確個中的道理,所以上面我們就簡略的舉一個例子來展現下。起首我們先完成IQueryProvider接口,個中會用到一個Query類,這個類會在前面停止引見,起首我們新建一個QueryProvider類完成IQueryProvider接口,起首我們看下CreateQuery<S>辦法:

       這裡的expression就是傳遞給我們,而且須要我們處置的表達式樹,最初還要前往完成IQueryable<S>接口的示例,以便LINQ在此基本長進行上面的查詢,這裡我們僅僅只是創立了一個Query的實例,同時將expression傳遞給它,由於此處僅僅只是一個DEMO,所以我們沒有去真正解析表達式樹(這個中要做的任務許多)。接著還有CreateQuery辦法:

 我們可以看到上面這句話:

現實的寄義就是創立Query<>的實例,而且泛型參數是elementType,參數是this和expression。

 最初就是Execute辦法了,傳遞一個Expression參數,並獲得最初的成果,筆者在這裡直接是寫逝世的值:

 Query類

      僅僅只要QueryProvider還沒用,我們還須要一個可以或許保留表達式樹狀況的類,固然也包含了我們解析表達式後的成果也能夠保留在個中,如許我們在IQueryProvider的Execute辦法中便可以依據我們解析的成果履行履行並前往成果了。

       這裡我們可以看到Query的Expression值在創立這個實例時,假如沒有傳遞Expression參數時該值就是:

       然則在前面的進程中Query中的Expression將是QueryProvider中的expression值。

       到此我們其實就完成了一個簡略的示例了,我們便可以開端測試我們的結果了,筆者在應用以下的代碼來測試:

      OK,我們開端看看是若何剖析這句LINQ語句的。

       起首我們看下在一開端履行時Query中Expression的前往值(以下圖):

      在獲得到這個表達式後,就開端履行Linq,起首履行的是where item == 123。

 剖析Where item == 123

     接著我們F5,便可以看到在QueryProvider中的CreateQuery<S>射中了,而且Expression參數以下圖所示:

      我們看到外面的字符串是 Where(item => (item == 123)),經由過程這句話我們便可以明確其實LINQ中的where本質上就是應用Where辦法,並傳遞給它對應的lambda表達式。剖析完了where部門,上面就是FirstOrDefault部門了。

 剖析FirstOrDefault

      當履行到FirstOrDefault的時刻我們可以檢查t的值,會發明t現實上就是QueryProvider中CreateQuery<S>的前往值。

 

      接著我們開端履行上面FirstOrDefault辦法,發明會再一次的去獲得Expression的值,而此時Expression的值就是下面CreateQuery<T>傳遞給我們的參數expression。

      然後在將這個表達式樹和由表達式樹表現FirstOrDefault辦法挪用的值拼接起來,並挪用QueryProvider中的Execute<S>辦法,我們可以看到這個時刻傳遞給我們的參數expression的值。

       至此一個簡略的流程就停止了,最初就是前往筆者寫逝世的123這個值了。

       經由過程下面這個例子我們根本懂得了其任務的流程,上面我們將一步一步的剖析我們這個where item == 123,固然我們將會用到遞歸,所以請年夜家整頓好本身的思緒,一步一步的看若何從一個表達式樹平分析這條語句。

 剖析表達式樹實戰

      起首我們一個剖析表達式樹的辦法,這個辦法我們暫且放在QueryProvider中:


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;
            }
        }
 

並在CreateQuery<S>中挪用這個辦法

      然後我們可以開端運轉測試了,為了可以或許讓讀者明確以後處置的表達式樹,所以鄙人面的截圖中將會包括AnalysisExpression中參數exp的值,如許可以便於讀者辨別以後處置的表達式樹。

 PS:Expression類型中的NodeType長短常主要的,由於傳遞給我們的都是父類Expression類型,而我們須要依據NodeType的轉換成對應的子類,如許我們能力夠獲得到更具體的信息。

 ExpressionType.Call

      我們依據一開端的exp的NodeType進入到這個分支,由於where本質上就是ss挪用where辦法,所以我們經由過程將exp轉換成對應的MethodCallExpression類型,如許我們便可以看到挪用的辦法稱號了。

      固然挪用一個辦法必需要有參數,所以上面還須要輪回Arguments去剖析詳細的參數,個中也包含挪用這個辦法的對象,天然我們起首是剖析挪用這個辦法的對象,這裡我們停止了第一次的遞歸挪用,跳到了ExpressionType.Constant。

 ExpressionType.Constant

      NodeType為這個類型,我們便可以經由過程ConstantExpression類型來獲得對應的參數,經由過程Value我們可以可以獲得到挪用where辦法的對象,固然到這裡就不會持續往下剖析了。

 

    所以我們持續跳到之前的for輪回,開端剖析第二個參數,就是 item => item == 123這個部門了。

 ExpressionType.Quote

      假如接觸過lambda的人能夠會以為類型應當是Lambda,但現實上不會直接跳轉到那,而是先跳轉到Quote,然後我們再把轉換成UnaryExpression類型,然後再持續剖析個中Operand屬性,而這個屬性的NodeType就是Lambda了。小我以為這個應當是辨別lambda和通俗的辦法,由於where不只僅可以吸收lambda同時也能夠是慣例的辦法,所以這裡還須要這一層。

 ExpressionType.Lambda

跳轉到這,年夜家就不會感到奇異了,這裡為了簡練。筆者並沒有剖析參數,而是直接剖析Body部門,由於這部門才是我們的症結。

 ExpressionType.Equal

      我們看到這個lambda很簡略,就是一個相等比擬,所以直接跳轉到了Equal,固然還有And、Or等對應的列舉,而到了這一步我們便可以直接剖析Left和Right,固然這裡還有一個小插曲,就是在跳到這個列舉的時刻我檢查exp的類型時,現實上是LogicalBinaryExpression類型,其實不是BinaryExpression類型,然後用Reflector檢查了下,我就呵呵了。

       我其時還奇異,怎樣沒有這個類型呢,最初才曉得玩的是這一出。到此為止,我們持續剖析這個相等操作的閣下雙方的參數吧。

起首剖析的是右邊參數item。

 ExpressionType.Parameter

      Item挑傳到這,並將其轉換成ParameterExpression類型,筆者在此僅僅只輸入了參數的稱號。

到這右邊的參數剖析終了,我們開端剖析左邊的參數。

 ExpressionType.Constant

      我們可以輕松的想到對應的Value就是123了,到此全部表達式就剖析終了了。

 我們看看最初掌握台的輸入成果吧。

       在此筆者還要聲明一個成績,就是我們應當去懂得我們應用的各類庫的道理,如許便於我們今後添加相符現實開辟的一些功效,固然這其實不是糟蹋時光。而是進步往後項目開辟的時光,跟著赓續的積聚,我們會發明許多反復的功效其實不須要我們去反復寫了,而節儉上去的時光我們便可以做本身想做的事了,所以我們要做一個有思惟的懶法式員。

 源碼下載

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved