您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何理解Entity Framework中的IQueryable和IQueryProvider接口”,在日常操作中,相信很多人在如何理解Entity Framework中的IQueryable和IQueryProvider接口問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解Entity Framework中的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>方法:
我們可以看到下面這句話:
實際的含義就是創建Query<>的實例,并且泛型參數是elementType,參數是this和expression。
最后就是Execute方法了,傳遞一個Expression參數,并獲取最后的結果,筆者在這里直接是寫死的值:
Query類
僅僅只有QueryProvider還沒用,我們還需要一個能夠保存表達式樹狀態的類,當然也包括了我們解析表達式后的結果也可以保存在其中,這樣我們在IQueryProvider的Execute方法中就可以根據我們解析的結果執行執行并返回結果了。
但是在后面的過程中Query中的Expression將是QueryProvider中的expression值。
OK,我們開始看看是如何分析這句LINQ語句的。
首先我們看下在一開始執行時Query中Expression的返回值(如下圖):
我們看到里面的字符串是 Where(item => (item == 123)),通過這句話我們就可以明白其實LINQ中的where實質上就是利用Where方法,并傳遞給它對應的lambda表達式。分析完了where部分,下面就是FirstOrDefault部分了。
分析FirstOrDefault
當執行到FirstOrDefault的時候我們可以查看t的值,會發現t實際上就是QueryProvider中CreateQuery<S>的返回值。
接著我們開始執行下面FirstOrDefault方法,發現會再一次的去獲取Expression的值,而此時Expression的值就是上面CreateQuery<T>傳遞給我們的參數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>中調用這個方法
當然調用一個方法必須要有參數,所以下面還需要循環Arguments去分析具體的參數,其中也包括調用這個方法的對象,自然我們首先是分析調用這個方法的對象,這里我們進行了第一次的遞歸調用,跳到了ExpressionType.Constant。
ExpressionType.Constant
NodeType為這個類型,我們就可以通過ConstantExpression類型來獲取對應的參數,通過Value我們可以可以獲取到調用where方法的對象,當然到這里就不會繼續往下分析了。
ExpressionType.Quote
如果接觸過lambda的人可能會認為類型應該是Lambda,但實際上不會直接跳轉到那,而是先跳轉到Quote,然后我們再把轉換成UnaryExpression類型,然后再繼續分析其中Operand屬性,而這個屬性的NodeType就是Lambda了。個人認為這個應該是區分lambda和普通的方法,因為where不僅僅可以接收lambda同時也可以是常規的方法,所以這里還需要這一層。
ExpressionType.Lambda
跳轉到這,大家就不會感覺奇怪了,這里為了簡潔。筆者并沒有分析參數,而是直接分析Body部分,因為這部分才是我們的關鍵。
我當時還奇怪,怎么沒有這個類型呢,最后才知道玩的是這一出。到此為止,我們繼續分析這個相等操作的左右兩邊的參數吧。
到這左邊的參數分析完畢,我們開始分析右邊的參數。
ExpressionType.Constant
我們可以輕松的想到對應的Value就是123了,到此整個表達式就分析完畢了。
到此,關于“如何理解Entity Framework中的IQueryable和IQueryProvider接口”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。