ASP.NET Web API 控制器建立過程(二)c#
前言api
原本這篇隨筆應該是在上週就該寫出來發佈的,因爲身體跟不上節奏感冒發燒有心無力,這種天氣感冒發燒生不如死,也真正的體會到了什麼叫病來如山倒,病去如抽絲。這兩天狀態纔好了一點,讓我理解了什麼纔是革命的本錢,但願你們也多保重身體。緩存
好了,仍是迴歸主題,對於上一篇的內容講解的只是ASP.NET Web API控制器建立過程當中的一個局部知識,在接着上篇內容講解的以前,我會先回顧一下上篇的內容,而且在本篇裏進行整合,讓咱們要看到的是一個整個的建立過程。服務器
ASP.NET Web API 控制器建立、激活過程app
l ASP.NET Web API 控制器建立過程(一)框架
l ASP.NET Web API 控制器建立過程(二)ide
圖1函數
在前面的篇幅中咱們說過APIController是由HttpControllerDispatcher類型來建立的,這只是表面上的,圖1中顯示的就是控制器建立的整個過程了,咱們先來回顧一下上一篇所講的,否則會以爲不連貫,在回顧的同時也會對圖1進行講解。spa
首先咱們來分解圖1,能夠把圖1中分爲兩個部分,xml
第一個部分就是HttpConfiguration類型所表示的部分。如圖2
圖2
先來解釋一下HttpConfiguration部分,在HttpConfiguration類型中有兩個屬性,第一個是ServicesContainer類型的屬性Services,第二個就是IDependencyResolver類型的屬性DependencyResolver,對於Services屬性的類型在上篇中我也說過了,就是一個IoC容器,從HttpConfiguration類型角度來看就是一個依賴注入到HttpConfiguration中的IoC容器,對於DependencyResolver屬性來講也差很少就是這個意思了。
只不過Services這個容器中存放的大多都是ASP.NET Web API框架中作一些基礎工做的類型。
就好像上篇中說到的,在ASP.NET Web API框架中加載控制器所在程序集的時候咱們就是使用自定義的工做項替換掉了Services容器中的默認工做項:
selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver), newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver());
這裏從圖2中能夠看出默認的DefaultAssembliesResolver類型來執行這項工做的。
到這裏也就是上個篇幅中的主要內容了。下面咱們仍是繼續分解圖1,上面說了第一部分了下面來看第二部分,第二個部分就是HttpControllerDispatcher類型到APIController類型的生成過程,也就是圖1了。
首先咱們的ASP.NET Web API框架會從HttpConfiguration中的Services容器中獲取一個ControllerSelector(控制器選擇器),這個控制器選擇器呢對應的類型你們從圖2中也能夠看到,圖1中也有,很明瞭。
那麼ControllerSelector主要幹什麼呢?確定是選擇控制器阿,固然了根據請求選擇相應的控制器是主要功能,次要功能是啥?次要功能是生成控制器緩存,否則從哪選阿對不。在ASP.NET MVC框架中控制器緩存是存在xml文件中的,如今很好奇在ASP.NET Web API框架中控制器緩存是什麼樣的存儲方式呢?
咱們就來看一下控制器選擇器的次要功能。
控制器選擇器次要功能
首先咱們先說明一下緩存的類型爲ConcurrentDictionary<string, HttpControllerDescriptor>類型,就是一個一一對應的鍵值隊,string表示着控制器名稱,而HttpControllerDescriptor表示着對應控制器的控制器描述類型,這個類型很重要稍後再說,咱們先要了解ConcurrentDictionary<string, HttpControllerDescriptor>緩存的由來。
首先在咱們控制器選擇器實例化的時候,在控制器選擇器的構造函數中已經使用了延遲加載技術對控制器緩存進行了建立,具體的建立過程能夠在圖1看到,是由DefaultAssembliesResolver類型(或者是咱們自定義的工做項)加載指定的程序集,而且交由DefaultHttpControllerTypeResolver類型根據ASP.NET Web API框架中默認的搜索過濾條件返回加載程序集中的全部符合條件的控制器類型(ControllerTypes),來看示例。
所用項目結構仍是上個篇幅的示例:
圖3
圖4
在圖4中咱們額外定義了一些控制器類型,而後在SelfHost端定義以下示例代碼:
代碼1-1
staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer) { ICollection<Type>types=selfHostServer.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(selfHostServer.Configuration.Services.GetAssembliesResolver()); foreach (Typetypeintypes) { Console.WriteLine(type.Namespace+"_______"+type.Name); } }
而且在註冊端調用此靜態函數:
using (HttpSelfHostServerselfHostServer=newHttpSelfHostServer(selfHostConfiguration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional }); selfHostServer.Configuration.Services.Replace(typeof(IAssembliesResolver), newCustomAssembliesResolver.LoadSpecifiedAssembliesResolver()); WriterControllerTypeMessage(selfHostServer); selfHostServer.OpenAsync(); Console.WriteLine("服務器端服務監聽已開啓"); Console.Read(); }
結果如圖5:
圖5
在咱們獲取了ControllerTypes事後了,ASP.NET Web API框架中有個HttpControllerTypeCache類型的對象就藏不住了,以前的一些操做都是由HttpControllerTypeCache類型去處理的,而在HttpControllerTypeCache獲取了ControllerTypes事後就要作一個很重要的工做了,就是對ControllerTypes進行分組操做最後返回一個Dictionary<string, ILookup<string, Type>>類型的對象,就拿上面的示例來講吧,最後通過分組後的Dictionary<string, ILookup<string, Type>>類型值應該是:
Writer-->NameSpaceControllerOne->WriterController
NameSpaceControllerTwo->WriterController
Read-->NameSpaceControllerOne->ReadController
WriterAndRead-->NameSpaceControllerThree->WriterAndReadController
Product-->WebAPIController->ProductController
這個時候的值並非最終的緩存類型,而是經過咱們的控制器選擇器根據HttpControllerTypeCache類型所生成的Dictionary<string,ILookup<string, Type>>類型值來生成ConcurrentDictionary<string, HttpControllerDescriptor>緩存類型,仍是根據上面的示例,咱們看一下最後生成的緩存類型值。
修改1-1以下示例代碼:
代碼1-2
staticvoidWriterControllerTypeMessage(HttpSelfHostServerselfHostServer) { ICollection<Type>types=selfHostServer.Configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(selfHostServer.Configuration.Services.GetAssembliesResolver()); foreach (Typetypeintypes) { Console.WriteLine(type.Namespace+"_______"+type.Name); } //Dictionary<string,ILookup<string, Type>> controllertypecache = types.GroupBy<Type,string>(t => t.Name,StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string,Type>, string, ILookup<string, Type>> // (g => g.Key, // g => g.ToLookup<Type,string>(t => (t.Namespace ?? string.Empty),StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); //foreach(var value in controllertypecache) //{ // foreach (var val in value.Value) // { // } //} IDictionary<string, HttpControllerDescriptor>mapping=selfHostServer.Configuration.Services.GetHttpControllerSelector().GetControllerMapping(); foreach (varmeginmapping) { Console.WriteLine("ControllerName:"+meg.Key+".ControllerTypeName:"+meg.Value.ControllerType.Name); } }
結果如圖6:
圖6
(在代碼1-2中註釋掉的部分就是能夠查看對ControllerTypes進行分組操做返回Dictionary<string, ILookup<string, Type>>類型的值)。
控制器選擇器主要功能
次要功能看完以後,主要功能想必你們也是很明瞭吧,在有了控制器緩存對象事後,控制器選擇器則會根據HttpRequestMessage對象中的路由數據對象獲取控制器名稱,而後從緩存中獲取到對應的HttpControllerDescriptor類型實例。
具體生成工做
在獲取到了HttpControllerDescriptor類型實例事後生成IHttpController的工做就變得很簡單了,仍是從HttpConfiguration中的Services容器中得到對應的負責控制器生成激活的工做項,在圖1中能夠明確的看出是DefaultHttpControllerActivator類型,在DefaultHttpControllerActivator類型工做的時候它會從HttpConfiguration中獲取DependencyResolver屬性對應的容器,若是這裏的狀況不知足纔會調用後面的TypeActivator來生成激活IHttpController(經過反射)。