溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Entity Framework中的IQueryable和IQueryProvider接口

發(fā)布時間:2021-10-12 09:43:51 來源:億速云 閱讀:125 作者:iii 欄目:編程語言

這篇文章主要介紹“如何理解Entity Framework中的IQueryable和IQueryProvider接口”,在日常操作中,相信很多人在如何理解Entity Framework中的IQueryable和IQueryProvider接口問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解Entity Framework中的IQueryable和IQueryProvider接口”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

 IQueryable接口

      我們先聊聊這個接口,因?yàn)槲覀冊谑褂肊F中經(jīng)??吹絣inq to sql語句的返回類型是IQueryable,我們可以看下這個接口的結(jié)構(gòu):

復(fù)制代碼代碼如下:

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

      或許會有人很奇怪,當(dāng)我們在開發(fā)過程中使用這個接口的時候,提供的方法遠(yuǎn)遠(yuǎn)不止這么點(diǎn),因?yàn)槲④浱峁┝藦?qiáng)大的Queryable類,當(dāng)然大家不要以為這個類是實(shí)現(xiàn)IQueryable然后實(shí)現(xiàn)了很多方法,如果是那樣那些第三方庫怎么自定義呢?所以Queryable只是一個靜態(tài)類,對IQueryable接口進(jìn)行了擴(kuò)展,下面是筆者在.Net Reflector截圖中一部分:

如何理解Entity Framework中的IQueryable和IQueryProvider接口

       如果讀者細(xì)心一點(diǎn)會發(fā)現(xiàn)linq to sql并不會導(dǎo)致實(shí)際的查詢,只有當(dāng)我們真正開始使用的時候才從數(shù)據(jù)庫中開始查詢數(shù)據(jù)。

IQueryProvider接口

      如果我們調(diào)試的EF的話,會看到生成的T-SQL語句。T-SQL就是根據(jù)表達(dá)式樹分析從而得出的,而核心就是IQueryProvider接口,下面就是該接口的結(jié)構(gòu):

復(fù)制代碼代碼如下:


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

      其中CreateQuery就是負(fù)責(zé)解析表達(dá)式樹的,當(dāng)然還要將處理后的結(jié)果返回,以便接著分析下面的語句,當(dāng)然這中間只是分析,你完全可以根據(jù)表達(dá)式樹得出你自己需要的查詢語句,比如SQL或者其他什么,只有在真正使用數(shù)據(jù)的時候才會調(diào)用Execute方法,這個時候就可以根據(jù)我們自己分析的語句開始進(jìn)行實(shí)際的查詢了。

 實(shí)例分析

QueryProvider類

      光說不練我們永遠(yuǎn)不能明白其中的原理,所以下面我們就簡單的舉一個例子來展示下。首先我們先實(shí)現(xiàn)IQueryProvider接口,其中會用到一個Query類,這個類會在后面進(jìn)行介紹,首先我們新建一個QueryProvider類實(shí)現(xiàn)IQueryProvider接口,首先我們看下CreateQuery<S>方法:

如何理解Entity Framework中的IQueryable和IQueryProvider接口

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

如何理解Entity Framework中的IQueryable和IQueryProvider接口

實(shí)際的含義就是創(chuàng)建Query<>的實(shí)例,并且泛型參數(shù)是elementType,參數(shù)是thisexpression。

 最后就是Execute方法了,傳遞一個Expression參數(shù),并獲取最后的結(jié)果,筆者在這里直接是寫死的值:

如何理解Entity Framework中的IQueryable和IQueryProvider接口

 Query類

      僅僅只有QueryProvider還沒用,我們還需要一個能夠保存表達(dá)式樹狀態(tài)的類,當(dāng)然也包括了我們解析表達(dá)式后的結(jié)果也可以保存在其中,這樣我們在IQueryProvider的Execute方法中就可以根據(jù)我們解析的結(jié)果執(zhí)行執(zhí)行并返回結(jié)果了。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

       但是在后面的過程中Query中的Expression將是QueryProvider中的expression值。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

      OK,我們開始看看是如何分析這句LINQ語句的。

       首先我們看下在一開始執(zhí)行時Query中Expression的返回值(如下圖):

如何理解Entity Framework中的IQueryable和IQueryProvider接口

      我們看到里面的字符串是 Where(item => (item == 123)),通過這句話我們就可以明白其實(shí)LINQ中的where實(shí)質(zhì)上就是利用Where方法,并傳遞給它對應(yīng)的lambda表達(dá)式。分析完了where部分,下面就是FirstOrDefault部分了。

 分析FirstOrDefault

      當(dāng)執(zhí)行到FirstOrDefault的時候我們可以查看t的值,會發(fā)現(xiàn)t實(shí)際上就是QueryProvider中CreateQuery<S>的返回值。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

      接著我們開始執(zhí)行下面FirstOrDefault方法,發(fā)現(xiàn)會再一次的去獲取Expression的值,而此時Expression的值就是上面CreateQuery<T>傳遞給我們的參數(shù)expression。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

       至此一個簡單的流程就結(jié)束了,最后就是返回筆者寫死的123這個值了。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

       通過上面這個例子我們基本了解了其工作的流程,下面我們將一步一步的分析我們這個where item == 123,當(dāng)然我們將會用到遞歸,所以請大家整理好自己的思路,一步一步的看如何從一個表達(dá)式樹中分析這條語句。

 分析表達(dá)式樹實(shí)戰(zhàn)

      首先我們一個分析表達(dá)式樹的方法,這個方法我們暫且放在QueryProvider中:

復(fù)制代碼代碼如下:


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>中調(diào)用這個方法

如何理解Entity Framework中的IQueryable和IQueryProvider接口

      當(dāng)然調(diào)用一個方法必須要有參數(shù),所以下面還需要循環(huán)Arguments去分析具體的參數(shù),其中也包括調(diào)用這個方法的對象,自然我們首先是分析調(diào)用這個方法的對象,這里我們進(jìn)行了第一次的遞歸調(diào)用,跳到了ExpressionType.Constant。

 ExpressionType.Constant

      NodeType為這個類型,我們就可以通過ConstantExpression類型來獲取對應(yīng)的參數(shù),通過Value我們可以可以獲取到調(diào)用where方法的對象,當(dāng)然到這里就不會繼續(xù)往下分析了。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

 ExpressionType.Quote

      如果接觸過lambda的人可能會認(rèn)為類型應(yīng)該是Lambda,但實(shí)際上不會直接跳轉(zhuǎn)到那,而是先跳轉(zhuǎn)到Quote,然后我們再把轉(zhuǎn)換成UnaryExpression類型,然后再繼續(xù)分析其中Operand屬性,而這個屬性的NodeType就是Lambda了。個人認(rèn)為這個應(yīng)該是區(qū)分lambda和普通的方法,因?yàn)閣here不僅僅可以接收lambda同時也可以是常規(guī)的方法,所以這里還需要這一層。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

 ExpressionType.Lambda

跳轉(zhuǎn)到這,大家就不會感覺奇怪了,這里為了簡潔。筆者并沒有分析參數(shù),而是直接分析Body部分,因?yàn)檫@部分才是我們的關(guān)鍵。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

       我當(dāng)時還奇怪,怎么沒有這個類型呢,最后才知道玩的是這一出。到此為止,我們繼續(xù)分析這個相等操作的左右兩邊的參數(shù)吧。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

到這左邊的參數(shù)分析完畢,我們開始分析右邊的參數(shù)。

 ExpressionType.Constant

      我們可以輕松的想到對應(yīng)的Value就是123了,到此整個表達(dá)式就分析完畢了。

如何理解Entity Framework中的IQueryable和IQueryProvider接口

到此,關(guān)于“如何理解Entity Framework中的IQueryable和IQueryProvider接口”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI