閱讀目錄:
創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷推廣、網(wǎng)站重做改版、荔浦網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5頁面制作、電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為荔浦等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
1.開篇介紹
2.ASP.NETMVC IControllerFactory 控制器工廠接口
3.ASP.NETMVC DefaultControllerFactory 默認(rèn)控制器工廠
4.ASP.NETMVC ControllerBuilder 控制器創(chuàng)建入口設(shè)置
5.ASP.NETMVC 自定義IControllerFactory
上一篇文章“.NET/ASP.NET MVC Controller 控制器(一:深入解析控制器運行原理)”詳細(xì)的講解了MvcHandler對象內(nèi)部的基本流程邏輯,這基本的流程邏輯為我們后面的學(xué)習(xí)起到鋪墊作用,當(dāng)我們能正確的搞懂它的內(nèi)部執(zhí)行流程后,我們就可以順藤摸瓜的去挖掘每個邏輯環(huán)節(jié)中的詳細(xì)邏輯;
通過前面兩篇文章的介紹,我們基本上能搞清楚一個Url請求是如何借助于UrlRoutingModule模塊順利穿過ASP.NET基礎(chǔ)框架到達(dá)應(yīng)用框架的過程,當(dāng)UrlRoutingModule處理過后將RouteData對象封裝在RequestContext請求上下文中傳入到MvcHandler對象,然后MvcHandler對象通過IControllerFactory接口根據(jù)從RouteData中獲取到controllername控制器名稱字符串創(chuàng)建具體的IController對象實例;
這基本的流程我們是清晰了,但是我們并不太清楚IControllerFactory背后所發(fā)生的一切,到底誰作為IControllerFactory默認(rèn)實現(xiàn)的,它又有著怎樣的擴(kuò)展入口讓我們來擴(kuò)展創(chuàng)建過程,這值得一探究竟;
那么這篇文章讓我們來分析一下IControllerFactory的背后所發(fā)生的事情,我們是否能從中學(xué)到什么設(shè)計思想;
既然能將ControllerFactory提取出接口來,那么對于IController的創(chuàng)建將是一個非常寬松的過程;簡單的設(shè)想一下,如果不將Factory提出接口來,那么對于IController的創(chuàng)建將是一個很直觀的過程,但是ASP.NETMVC將IController創(chuàng)建不是簡單的使用一個ControllerFactory來解決,而是將這個創(chuàng)建過程設(shè)計的很松散,目的是為了擴(kuò)展性方便,換句話說我們完全可以自定義一個Factroy來替代這個創(chuàng)建過程,也可以基于系統(tǒng)內(nèi)部的Factroy來擴(kuò)展一下;
MvcHandler使用IControllerFactroy創(chuàng)建出相應(yīng)IController對象,那么首先我們需要搞清楚MvcHandler通過什么方式獲取到實現(xiàn)IControllerFactory接口的;
其實在MvcHandler中并不是直接使用IControllerFactroy的相關(guān)實現(xiàn),而是使用了ControllerBuilder對象,這個對象是一個單例模式的實現(xiàn);MvcHanlder通過ControllerBuilder對象獲取到一個實例,然后通過ControllerBuilder創(chuàng)建出IControllerFactory實現(xiàn);
internal ControllerBuilder ControllerBuilder { get { if (_controllerBuilder == null) { _controllerBuilder = ControllerBuilder.Current; } return _controllerBuilder; } set { _controllerBuilder = value; } } factory = ControllerBuilder.GetControllerFactory();
可以簡單的理解為,ControllerBuilder管理著IControllerFactory的創(chuàng)建過程,MvcHanlder通過獲取ControllerBuilder的全局實例,然后調(diào)用其方法GetControllerFactory,得到可以使用的IControllerFactory實現(xiàn);
圖1:
ControllerBuilder的設(shè)計很巧妙,它將IControllerFactory的實現(xiàn)為我們敞開了大門,我們可以通過這個入口做很多事情;
我們看一下IControllerFactroy接口的定義:
public interface IControllerFactory { IController CreateController(RequestContext requestContext, string controllerName); SessionStateBehavior GetControllerSessionRequestContext requestContext, string controllerName); void ReleaseController(IController controller); }
接口中定義了三個方法,第一個方法CreateController很好理解,根據(jù)方法的第二個參數(shù)controllerName創(chuàng)建Controller實例;第二個方法GetControllerSessionBehavior方法是用來獲取controllerName所代表的Controller的Session行為的,該行為是通過SessionStateAttribute特性表示;第三個方法ReleaseController方法是用在最后釋放Controller的:
public virtual void ReleaseController(IController controller) { IDisposable disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } }
由于Controller繼承自IDisposable接口,所以在方法內(nèi)部是直接調(diào)用Dispose方法來釋放資源;這里需要注意的是,Controller對IDisposable接口的實現(xiàn)是virtual修飾符:
protected virtual void Dispose(bool disposing) { }
這就很方便我們通過重寫此方法的方式來釋放一些其他資源;
在ASP.NETMVC內(nèi)部有一個默認(rèn)的Factroy(DefaultControllerFactroy),DefaultControllerFactroy實現(xiàn)了核心的創(chuàng)建IController代碼,這為我們的擴(kuò)展提供了很好的接口;
通過調(diào)用IControllerFactory接口的CreateController(RequestContext requestContext, string controllerName) 方法,將進(jìn)入到DefaultControllerFactory實現(xiàn)中,首要任務(wù)就是要根據(jù)controllerName名稱找到對應(yīng)的ContorllerType,然后才能創(chuàng)建具體的實例;
object routeNamespacesObj; Type match; if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) { IEnumerablerouteNamespaces = routeNamespacesObj as IEnumerable ; if (routeNamespaces != null && routeNamespaces.Any()) { HashSet nsHash = new HashSet (routeNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true" if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) { // got a match or the route requested we stop looking return match; } } }
首先根據(jù)請求的路由數(shù)據(jù)RouteData,查找設(shè)置的命名空間集合,然后使用命名空間和控制器名稱獲取Type,如果Type!=null并且沒有開啟后被命名空間則直接返回Type;
在DefaultControllerFactroy內(nèi)部使用到了兩組命名空間來作為查找Controller的NameSpace,第一個是我們在配置Route數(shù)據(jù)的時候設(shè)置的:
context.MapRoute(name: "api.order.default", url: "api/order/{controller}/{action}/{orderid}", defaults: new { controller = "OrderController", action = "GetOrderOperationDatetime", orderid = "1001" }, namespaces: new string[] { "Api.Order" });
而第二個我們一般都不會用它的,它是作為AreaRegistration后備命名空間而存在的,是在ControllerBuilder中設(shè)置的:
ControllerBuilder.Current.DefaultNamespaces.Add("MvcApplication4.ApiOrder");
對后備命名空間的賦值是在AreaRegistrationContext中的MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) 方法中完成的:
if (namespaces == null && Namespaces != null) { namespaces = Namespaces.ToArray(); } Route route = Routes.MapRoute(name, url, defaults, constraints, namespaces); route.DataTokens["area"] = AreaName; // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up // controllers belonging to other areas bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0); route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback; return route;
由于AreaRegistration可以讓我們對Controller的設(shè)計不局限于ASP.NETMVCWeb程序中,而可以將Controller獨立出去進(jìn)行模塊化設(shè)計,所以需要提供有關(guān)Area的特殊命名空間查找方式;
ControllerBuilder作為Controller創(chuàng)建的設(shè)置入口,可以用來設(shè)置ControllerFactory替換系統(tǒng)默認(rèn)的DefaultControllerFactory,ControllerBuilder是Controller的創(chuàng)建過程框架擴(kuò)展入口,可以借助ControllerBuilder方便做很多設(shè)置;
internal ControllerBuilder(IResolverserviceResolver) { _serviceResolver = serviceResolver ?? new SingleServiceResolver ( () => _factoryThunk(), new DefaultControllerFactory { ControllerBuilder = this }, "ControllerBuilder.GetControllerFactory" ); }
在ControllerBuilder的構(gòu)造函數(shù)中,初始化了一個SingleServiceResolver
public HashSetDefaultNamespaces { get { return _namespaces; } }
在此我們可以設(shè)置統(tǒng)一的命名空間,由于我們在設(shè)置Route的時候,都需要設(shè)置namesapce字段,但是如果有很多這樣的Route的時候就很麻煩,我們可以通過此方式進(jìn)行統(tǒng)一的設(shè)置;
public void SetControllerFactory(IControllerFactory controllerFactory) { if (controllerFactory == null) { throw new ArgumentNullException("controllerFactory"); } _factoryThunk = () => controllerFactory; }
還有一個比較重要的就是設(shè)置自定義的ControllerFactory,在方法SetControllerFactory中,我們可以設(shè)置一個IControllerFactory類型的對象,就可以接管系統(tǒng)默認(rèn)的DefaultControllerFactory對象,包括后面的所有的IController緩存策略;
圖2:
基本上我們可以通過ControllerBuilder進(jìn)入到ControllerFactroy的創(chuàng)建環(huán)節(jié)來,使用SetControllerFactory方法直接將我們自定義的IControllerFactroy傳入即可;
既然知道了ContollerBulder可以使我們更改系統(tǒng)默認(rèn)的控制器工廠,那么我們通過怎樣的方式使用現(xiàn)在的Factroy;大致上我們只需要繼承自DefaultControllerFactory然后進(jìn)行相應(yīng)的擴(kuò)展即可;
public class CustomControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) { Console.WriteLine(string.Format("{0}is create.", controllerType.Name)); return base.GetControllerInstance(requestContext, controllerType); } }
現(xiàn)在假設(shè)我們需要在系統(tǒng)創(chuàng)建所有Controller的時候能記錄下創(chuàng)建的記錄信息,這樣就很方便的完成了,我們只需要在系統(tǒng)初始化的地方進(jìn)行設(shè)置:
ControllerBuilder.Current.SetControllerFactory(new Api.Order.CustomControllerFactory());
這樣我們就接管了ControllerFactory的部分功能;
作者:王清培
出處:http://wangqingpei557.blog.51cto.com/
本文版權(quán)歸作者和51CTO共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。