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了,到此全部表達式就剖析終了了。
我們看看最初掌握台的輸入成果吧。
在此筆者還要聲明一個成績,就是我們應當去懂得我們應用的各類庫的道理,如許便於我們今後添加相符現實開辟的一些功效,固然這其實不是糟蹋時光。而是進步往後項目開辟的時光,跟著赓續的積聚,我們會發明許多反復的功效其實不須要我們去反復寫了,而節儉上去的時光我們便可以做本身想做的事了,所以我們要做一個有思惟的懶法式員。
源碼下載