AreaRegistration.RegisterAllAreas()
?我們新建一個名稱為Admin的Area,VS生成下面的代碼。
???????{?action?=?,?id?=
我們先來看AreaRegistration這個抽象類,實際上,它只有一個核心功能,就是RegisterAllAreas,獲取所有繼承它的子類類型,然后創建它,在為他創建一個AreaRegistrationContext,在調用它的RegisterArea方法。
?????TypeCacheName?=????AreaName?{????RegisterAllAreas(RouteCollection?routes,?IBuildManager?buildManager,?<Type>?areaRegistrationTypes?=?(Type?areaRegistrationType?=??CreateContextAndRegister(RouteCollection?routes,?=??thisNamespace?=?(thisNamespace?!=?+
為什么要有AreaRegistrationContext這個類型呢?假如沒有它,AreaRegistration子類創建完成時,就可以直接注冊了,我們的AdminAreaRegistration的RegisterArea方法完全可以通過RouteCollection再重載一個MapRoute方法用于Area路由的注冊。像下面這個樣子。
???{?action?=?,?id?=
?這樣不是很好么?跟隨著源碼,詳細瞧一瞧這個AreaRegistrationContext
AreaRegistrationContext
?這個類本質上只有一個屬性,那就是命名空間。
???HashSet<>?_namespaces?=??HashSet<>?AreaRegistrationContext(?areaName,?RouteCollection?routes,???AreaName?{?;???ICollection<>?{??RouteCollection?Routes?{?;????State?{?;???Route?MapRoute(?name,??url,??defaults,??constraints,
?我們回到核心的RegisterAllAreas方法中。
??(AreaRegistration).IsAssignableFrom(type)?&&!=????RegisterAllAreas(RouteCollection?routes,?IBuildManager?buildManager,?<Type>?areaRegistrationTypes?=?(Type?areaRegistrationType?=
通過TypeCacheUtil.GetFilteredTypesFromAssemblies獲取出來的類型必須符合IsAreaRegistrationType委托,(AreaRegistration).IsAssignableFrom(type)不難理解,必須是AreaRegistration的子類,那type.GetConstructor(Type.EmptyTypes)呢?其實一開始我也不明白它是什么意思,后來通過Console寫了個小程序測試了下。
??Main(=??tac?==??tbc?==??tcc?=?+?(tac?!=??+?(tbc?!=??+?(tcc?!=
View Code
?輸出:
類TA?:False
類TB?:False
類TC?:True
請按任意鍵繼續.?.?.
我們可以明白了,也就是我們的AdminAreaRegistration不能有構造器(Visual Studio生成的確實沒有構造器)。但是這里為什么要這樣約定呢?確實想不通,我們先繼續回到剛剛的TypeCacheUtil.GetFilteredTypesFromAssemblies方法。首先,會嘗試從緩存中獲取類型,與往常不同的是,這里緩存的格式是xml文件,緩存的原因應該很容易理解,頻繁反射會造成性能的影響,改良反射的方式有多種,這里我們學到了一種,緩存。關于TypeCacheSerializer如何工作和ReadTypesFromCache具體是如何實現的這里就不去看了,主要就是一些關于Stream和XmlDocument這兩個類的操作。但是有必要提一下IBuildManager這個接口。在MVC中的實現者是BuildManagerWrapper,內部實際使用的是BuildManager(位于System.Web.Compilation),關于它的詳細資料少之又少,只知道主要負責站點的動態編譯和程序集的管理。我們知道可以通過AppDomain來獲取應用程序相關的程序集,但這里為什么用BuilderManager呢?想必必有什么不同!
??IEnumerable<Type>?FilterTypesInAssemblies(IBuildManager?buildManager,?Predicate<Type>IEnumerable<Type>?typesSoFar?==?(Assembly?assembly?===?typesSoFar.Where(type?=>?TypeIsPublicClass(type)?&&
我們看到這里用它獲取所有的應用程序集。在foreach前打一個斷點。借助即時窗口我們可以和AppDomain獲取的程序集進行一個比較。
string[]?Arr1?=?assemblies.Cast().Select(a=>a.FullName).ToArray();
已計算表達式,表達式沒有值
string[]?Arr2?=?AppDomain.CurrentDomain.GetAssemblies().Select(a=>a.FullName).ToArray();
已計算表達式,表達式沒有值
Arr1.Length
36
Arr2.Length
42
string[]?Arr3?=?Arr2.Except(Arr1).ToArray();
已計算表達式,表達式沒有值
Arr3
{string[6]}
????[0]:?"System.Runtime.Caching,?Version=4.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
????[1]:?"Microsoft.Build.Utilities.v4.0,?Version=4.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
????[2]:?"Microsoft.JScript,?Version=10.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
????[3]:?"Microsoft.VisualStudio.Web.PageInspector.Runtime,?Version=15.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
????[4]:?"Microsoft.VisualStudio.Web.PageInspector.Tracing,?Version=15.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
? ? [5]:?"Microsoft.VisualStudio.Debugger.Runtime,?Version=15.0.0.0,?Culture=neutral,?PublicKeyToken=b03f5f7f11d50a3a"
這里列出的幾個命名空間我也不熟悉,但是大致可以了解,使用AppDomain返回的程序集是當前AppDomain下所有程序中顯示使用過的類型所在的程序集(如果你對AppDomain有了解,希望不要被我誤解),而BuildManager返回的是和程序運行環境甚至配置(調試)相關的程序集,我們可以這么理解,BuildManager提供更強大的功能,可以負責站點的動態編譯和程序集的管理。關于AreaRegistration類型的緩存我們基本已經了解,拿到所有的AreaRegistration類型后,我們針對每一個進行一次路由配置工作。
???RegisterAllAreas(RouteCollection?routes,?IBuildManager?buildManager,?<Type>?areaRegistrationTypes?=?(Type?areaRegistrationType?=
具體的
??CreateContextAndRegister(RouteCollection?routes,?=??thisNamespace?=?(thisNamespace?!=?+
我們來思考一下,這個thisNamespace會是什么值呢?由于這里的GetType目標是AdminAreaRegistration,(在我這里)所以是Mvc_Web.Areas.Admin,然后會被添加到這里的AreaRegistrationContext的Namespace屬性中,然后調用子類重寫的RegisterArea方法,最終添加到RouteCollection中,我們看最后調用的MapRoute方法。
?Route?MapRoute(?name,??url,??defaults,??constraints,??(namespaces?==??&&?Namespaces?!=?===?useNamespaceFallback?=?(namespaces?==??||?namespaces.Length?==?=
最重要的是倒數第二行和倒數第三行,他和控制器的匹配有關,其實根據UseNamespaceFallback這個也很容易理解,如果我們的AdminAreaRegistration沒有命名空間,那就允許它退回(到其他地方找)。
原文來自:技術之家