日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET MVC Controller激活系统详解:默认实现

發(fā)布時(shí)間:2025/5/22 asp.net 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET MVC Controller激活系统详解:默认实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Controller激活系統(tǒng)最終通過注冊的ControllerFactory創(chuàng)建相應(yīng)的Conroller對象,如果沒有對ControllerFactory類型或者類型進(jìn)行顯式注冊(通過調(diào)用當(dāng)前ControllerBuilder的SetControllerFactory方法),默認(rèn)使用的是一個(gè)DefaultControllerFactory對象,我們現(xiàn)在就來討論實(shí)現(xiàn)在DefaultControllerFactory類型中的默認(rèn)Controller激活機(jī)制。

目錄
一、Controller類型的解析
??? 實(shí)例演示:創(chuàng)建一個(gè)自定義ControllerFactory模擬Controller默認(rèn)激活機(jī)制
二、 Controller類型的緩存
三、 Controller的釋放
四、會(huì)話狀態(tài)行為的控制

一、Controller類型的解析

激活目標(biāo)Controller對象的前提是能夠正確解析出對應(yīng)的Controller類型。對于DefaultControllerFactory來,用于解析目標(biāo)Controller類型的信息包括:通過與當(dāng)前請求匹配的路由對象生成的RouteData(其中包含Controller的名稱和命名空間)和包含在當(dāng)前ControllerBuilder中的命名空間。很對讀者可以首先想到的是通過Controller名稱得到對應(yīng)的類型,并通過命名空間組成Controller類型的全名,最后遍歷所有程序集以此名稱去加載相應(yīng)的類型即可。

這貌似一個(gè)不錯(cuò)的解決方案,實(shí)際上則完全不可行。不要忘了作為請求地址URL一部分的Controller名稱是不區(qū)分大小寫的,而類型名稱則是區(qū)分大小的;不論是注冊路由時(shí)指定的命名空間還是當(dāng)前ControllerBuilder的默認(rèn)命名空間,有可能是包含統(tǒng)配符(*)。由于我們不能通過給定的Controller名稱和命名空間得到Controller的真實(shí)類型名稱,自然就不可能通過名稱去解析Controller的類型了。

ASP.NET MVC的Controller激活系統(tǒng)反其道而行之。它先遍歷通過BuildManager的靜態(tài)方法GetReferencedAssemblies方法得到的編譯Web應(yīng)用所使用的程序集,通過反射得到所有實(shí)現(xiàn)了接口IController的類型,最后通過給定的Controller的名稱和命名空間作為匹配條件在這個(gè)預(yù)先獲取的類型列表中得到目標(biāo)Controller的類型。

實(shí)例演示:創(chuàng)建一個(gè)自定義ControllerFactory模擬Controller默認(rèn)激活機(jī)制

為了讓讀者對默認(rèn)采用的Controller激活機(jī)制,尤其是Controller類型的解析機(jī)制有一個(gè)深刻的認(rèn)識(shí),我們通過一個(gè)自定義的ControllerFactory來模擬其中的實(shí)現(xiàn)。由于我們采用反射的方式來創(chuàng)建Controller對象,所以我們將該自定義ControllerFactory起名為ReflelctionControllerFactory。[源代碼從這里下載]

1: public class ReflelctionControllerFactory : IControllerFactory 2: { 3: //其他成員 4: private static List<Type> controllerTypes; 5: static ReflelctionControllerFactory() 6: { 7: controllerTypes = new List<Type>(); 8: foreach (Assembly assembly in BuildManager.GetReferencedAssemblies()) 9: { 10: controllerTypes.AddRange(assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type))); 11: } 12: } 13:? 14: public IController CreateController(RequestContext requestContext, string controllerName) 15: { 16: Type controllerType = this.GetControllerType(requestContext.RouteData, controllerName); 17: if (null == controllerType) 18: { 19: throw new HttpException(404, "No controller found"); 20: } 21: return (IController)Activator.CreateInstance(controllerType); 22: } 23:? 24: private static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace) 25: { 26: if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase)) 27: { 28: return string.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase); 29: } 30: requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length); 31: if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase)) 32: { 33: return false; 34: } 35: return ((requestedNamespace.Length == targetNamespace.Length) || (targetNamespace[requestedNamespace.Length] == '.')); 36:???? } 37:? 38: private Type GetControllerType(IEnumerable<string> namespaces, Type[] controllerTypes) 39: { 40: var types = (from type in controllerTypes 41: where namespaces.Any(ns => IsNamespaceMatch(ns, type.Namespace)) 42: select type).ToArray(); 43: switch (types.Length) 44: { 45: case 0: return null; 46: case 1: return types[0]; 47: default: throw new InvalidOperationException("Multiple types were found that match the requested controller name."); 48: } 49: } 50:? 51: protected virtual Type GetControllerType(RouteData routeData, string controllerName) 52: { 53: //省略實(shí)現(xiàn) 54: } 55: }

如上面的代碼片斷所示,ReflelctionControllerFactory具有一個(gè)靜態(tài)的controllerTypes字段由于保存所有Controller的類型。在靜態(tài)構(gòu)造函數(shù)中,我們調(diào)用BuildManager的GetReferencedAssemblies方法得到所有用于編譯Web應(yīng)用的程序集,并從中得到所有實(shí)現(xiàn)了IController接口的類型,這些類型全部被添加到通過靜態(tài)字段controllerTypes表示的類型列表。

Controller類型的解析實(shí)現(xiàn)在受保護(hù)的GetControllerType方法中,在用于最終激活Controller對象的CreateController方法中,我們通過調(diào)用該方法得到與指定RequestContext和Controller名稱相匹配的Controller類型,最終通過調(diào)用Activator的靜態(tài)方法CreateInstance根據(jù)該類型創(chuàng)建相應(yīng)的Controller對象。如果不能找到匹配的Controller類型(GetControllerType方法返回Null),則拋出一個(gè)HTTP狀態(tài)為404的HttpException。

ReflelctionControllerFactory中定義了兩個(gè)輔助方法,IsNamespaceMatch用于判斷Controller類型真正的命名空間是否與指定的命名空間(可能包含統(tǒng)配符)相匹配,在進(jìn)行字符比較過程中是忽略大小寫的。私有方法GetControllerType根據(jù)指定的命名空間列表和類型名稱匹配的類型數(shù)組得到一個(gè)完全匹配的Controller類型。如果得到多個(gè)匹配的類型,直接拋出InvalidOperation異常,并提示具有多個(gè)匹配的Controller類型;如果找不到匹配類型,則返回Null。

在如下所示的用于解析Controller類型的GetControllerType方法中,我們從預(yù)先得到的所有Controller類型列表中篩選出類型名稱與傳入的Controller名稱相匹配的類型。我們首先通過路由對象的命名空間對 之前 得到的類型列表進(jìn)行進(jìn)一步篩選,如果能夠找到一個(gè)唯一的類型,則直接將其作為Controller的類型返回。為了確定是否采用后備命名空間對Controller類型進(jìn)行解析,我們從作為參數(shù)參數(shù)的RouteData對象的DataTokens中得到獲取一個(gè)Key為“UseNamespaceFallback”的元素,如果該元素存在并且值為False,則直接返回Null。

如果RouteData的DataTokens中不存在這樣一個(gè)UseNamespaceFallback元素,或者它的值為True,則首先里當(dāng)前ControllerBuilder的默認(rèn)命名空間列表進(jìn)一步對Controller類型進(jìn)行解析,如果存在唯一的類型則直接當(dāng)作目標(biāo)Controller類型返回。如果通過兩組命名空間均不能得到一個(gè)匹配的ControllerType,并且只存在唯一一個(gè)與傳入的Controller名稱相匹配的類型,則直接將該類型作為目標(biāo)Controller返回。如果這樣的類型具有多個(gè),則直接拋出InvalidOperationException異常。

1: public class ReflelctionControllerFactory : IControllerFactory 2: { 3: //其他成員 4: protected virtual Type GetControllerType (RouteData routeData, string controllerName) 5: { 6: //根據(jù)類型名稱篩選 7: var types = controllerTypes.Where(type => string.Compare(controllerName + "Controller", type.Name, true) == 0).ToArray(); 8: if (types.Length == 0) 9: { 10: return null; 11: } 12:? 13: //通過路由對象的命名空間進(jìn)行匹配 14: var namespaces = routeData.DataTokens["Namespaces"] as IEnumerable<string>; 15: namespaces = namespaces ?? new string[0]; 16: Type contrllerType = this.GetControllerType(namespaces, types); 17: if (null != contrllerType) 18: { 19: return contrllerType; 20: } 21:? 22: //是否允許采用后備命名空間 23: bool useNamespaceFallback = true; 24: if (null != routeData.DataTokens["UseNamespaceFallback"]) 25: { 26: useNamespaceFallback = (bool)(routeData.DataTokens["UseNamespaceFallback"]); 27: } 28:? 29: //如果不允許采用后備命名空間,返回Null 30: if (!useNamespaceFallback) 31: { 32: return null; 33: } 34:? 35: //通過當(dāng)前ControllerBuilder的默認(rèn)命名空間進(jìn)行匹配 36: contrllerType = this.GetControllerType(ControllerBuilder.Current.DefaultNamespaces, types); 37: if (null != contrllerType) 38: { 39: return contrllerType; 40: } 41:? 42: //如果只存在一個(gè)類型名稱匹配的Controller,則返回之 43: if (types.Length == 1) 44: { 45: return types[0]; 46: } 47:? 48: //如果具有多個(gè)類型名稱匹配的Controller,則拋出異常 49: throw new InvalidOperationException("Multiple types were found that match the requested controller name."); 50: } 51: }

二、 Controller類型的緩存

為了避免通過遍歷所有程序集對目標(biāo)Controller類型的解析,ASP.NET MVC對解析出來的Controller類型進(jìn)行了緩存以提升性能。與針對用于Area注冊的AreaRegistration類型的緩存類似,Controller激活系統(tǒng)同樣采用基于文件的緩存策略,而用于保存Controller類型列表的名為MVC-ControllerTypeCache.xml的文件保存在ASP.NET的臨時(shí)目錄下面。具體的路徑如下,其中第一個(gè)針對寄宿于IIS中的Web應(yīng)用,后者針對直接通過Visual Studio Developer Server作為宿主的應(yīng)用。而用于保存所有AreaRegistration類型列表的MVC-AreaRegistrationTypeCache.xml文件也保存在這個(gè)目錄下面。

  • %Windir%\Microsoft.NET\Framework\v{version}\Temporary ASP.NET Files\{appname}\...\...\UserCache\
  • %Windir%\Microsoft.NET\Framework\v{version}\Temporary ASP.NET Files\root\...\...\UserCache\

對針對Web應(yīng)用被啟動(dòng)后的第一個(gè)請求時(shí),Controller激活系統(tǒng)會(huì)讀取這個(gè)用于緩存所有Controller類型列表的ControllerTypeCache.xml文件并反序列化成一個(gè)List<Type>對象。只有在該列表為空的時(shí)候才會(huì)通過遍歷程序集和反射的方式得到所有實(shí)現(xiàn)了接口IController的公有類型,而被解析出來的Controller類型重寫被寫入ControllerTypeCache.xml文件中。這個(gè)通過讀取緩存文件或者重新解析出來的Controller類型列表被保存到內(nèi)容中,在Web應(yīng)用活動(dòng)期間內(nèi)被Controller激活系統(tǒng)所用。

下面的XML片斷反映了這個(gè)用于Controller類型列表緩存的ControllerTypeCache.xml文件的結(jié)構(gòu),我們可以看出它包含了所有的Controller類型的全名和所在的程序集和托管模塊信息。

1: <?xml version="1.0" encoding="utf-8"?> 2: <!--This file is automatically generated. Please do not modify the contents of this file.--> 3: <typeCache lastModified="3/22/2012 1:18:49 PM" mvcVersionId="80365b23-7a1d-42b2-9e7d-cc6f5694c6d1"> 4: <assembly name="Artech.Admin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> 5: <module versionId="eb343e3f-2d63-4665-a12a-29fb30dceeed"> 6: <type> Artech.Admin .HomeController</type> 7: <type> Artech.Admin .EmployeeController </type> 8: </module> 9: </assembly> 10: <assembly name="Artech.Portal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> 11: <module versionId=" 3717F116-35EE-425F-A1AE-EB4267497D8C "> 12: <type>Artech. Portal.Controllers.HomeController</type> 13: <type>Artech. Portal.ProductsController</type> 14: </module> 15: </assembly> 16: </typeCache>

三、 Controller的釋放

作為激活Controller對象的ControllerFactory不僅僅用于創(chuàng)建目標(biāo)Controller對象,還具有兩個(gè)額外的功能,即通過ReleaseController方法對激活的Controller對象進(jìn)行釋放和回收,以及通過GetControllerSessionBehavior返回用于控制當(dāng)前會(huì)話狀態(tài)行為的SessionStateBehavior枚舉。

對于默認(rèn)使用DefaultControllerFactory來說,針對Controller對象的釋放操作很簡單:如果Controller類型實(shí)現(xiàn)了IDisposable接口,則直接調(diào)用其Dispose方法即可;否則直接忽略。我們將這個(gè)邏輯也實(shí)現(xiàn)在了我們自定義的ReflelctionControllerFactory中。

1: public class ReflelctionControllerFactory : IControllerFactory 2: { 3: //其他操作 4: public void ReleaseController(IController controller) 5: { 6: IDisposable disposable = controller as IDisposable; 7: if (null != disposable) 8: { 9: disposable.Dispose(); 10: } 11: } 12: }

四、會(huì)話狀態(tài)行為的控制

至于用于返回SessionStateBehavior枚舉的GetControllerSessionBehavior方法來說,在默認(rèn)的情況下的返回值為SessionStateBehavior.Default。通過前面的介紹我們知道在這種情況下具體的會(huì)話狀態(tài)行為取決于創(chuàng)建的HttpHandler所實(shí)現(xiàn)的標(biāo)記接口。對于ASP.NET MVC應(yīng)用來說,默認(rèn)用于處理請求的HttpHandler是一個(gè)叫做MvcHandler的對象,如下面的代碼片斷所示,HttpHandler實(shí)現(xiàn)了IRequiresSessionState接口,意味著默認(rèn)情況下會(huì)話狀態(tài)是可讀寫的(相當(dāng)于SessionStateBehavior.Requried)。

1: public class MvcHandler : 2: IHttpAsyncHandler, 3: IHttpHandler, 4: IRequiresSessionState 5: { 6: //其他成員 7: }

不過我們可以通過在Controller類型上應(yīng)用SessionStateAttribute特性來具體控制會(huì)話狀態(tài)行為。如下面的代碼片斷所示,SessionStateAttribute具有一個(gè)SessionStateBehavior類型的只讀屬性Behavior用于返回具體行為設(shè)置的會(huì)話狀態(tài)行為選項(xiàng),該屬性是在構(gòu)造函數(shù)中被初始化的。

1: [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 2: public sealed class SessionStateAttribute : Attribute 3: { 4: public SessionStateAttribute(SessionStateBehavior behavior); 5: public SessionStateBehavior Behavior { get; } 6: }

也就是說DefaultControllerFactory會(huì)通過解析出來的Controller類型得到應(yīng)用在上面的SessionStateAttribute特性,如果這樣的特性存在則直接返回它的Behavior屬性所表示的SessionStateBehavior枚舉;如果不存在則返回SessionStateBehavior.Default,具體的邏輯反映在我們自定義的ReflelctionControllerFactory的GetControllerSessionBehavior方法中。

1: public class ReflelctionControllerFactory : IControllerFactory 2: { 3: //其他成員 4: public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) 5: { 6: Type controllerType = this.GetControllerType(requestContext.RouteData, controllerName); 7: if (null == controllerType) 8: { 9: return SessionStateBehavior.Default; 10: } 11: SessionStateAttribute attribute = controllerType.GetCustomAttributes(true).OfType<SessionStateAttribute>() 12: .FirstOrDefault(); 13: attribute = attribute ?? new SessionStateAttribute(SessionStateBehavior.Default); 14: return attribute.Behavior; 15: } 16: }
ASP.NET MVC Controller激活系統(tǒng)詳解:總體設(shè)計(jì)
ASP.NET MVC Controller激活系統(tǒng)詳解:默認(rèn)實(shí)現(xiàn)
ASP.NET MVC Controller激活系統(tǒng)詳解:IoC的應(yīng)用[上篇]
ASP.NET MVC Controller激活系統(tǒng)詳解:IoC的應(yīng)用[下篇]

作者:蔣金楠
微信公眾賬號(hào):大內(nèi)老A
微博:www.weibo.com/artech
如果你想及時(shí)得到個(gè)人撰寫文章以及著作的消息推送,或者想看看個(gè)人推薦的技術(shù)資料,可以掃描左邊二維碼(或者長按識(shí)別二維碼)關(guān)注個(gè)人公眾號(hào)(原來公眾帳號(hào)蔣金楠的自媒體將會(huì)停用)。
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 原文鏈接

總結(jié)

以上是生活随笔為你收集整理的ASP.NET MVC Controller激活系统详解:默认实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。