.NET/ASP.NET MVC Controller 控制器(IController控制器的建立過程)

閱讀目錄:html

  • 1.開篇介紹
  • 2.ASP.NETMVC IControllerFactory 控制器工廠接口
  • 3.ASP.NETMVC DefaultControllerFactory 默認控制器工廠
  • 4.ASP.NETMVC ControllerBuilder 控制器建立入口設置
  • 5.ASP.NETMVC 自定義IControllerFactory

1】開篇介紹

上一篇文章「.NET/ASP.NET MVC Controller 控制器(一:深刻解析控制器運行原理)」詳細的講解了MvcHandler對象內部的基本流程邏輯,這基本的流程邏輯爲咱們後面的學習起到鋪墊做用,當咱們能正確的搞懂它的內部執行流程後,咱們就能夠順藤摸瓜的去挖掘每一個邏輯環節中的詳細邏輯;api

經過前面兩篇文章的介紹,咱們基本上能搞清楚一個Url請求是如何藉助於UrlRoutingModule模塊順利穿過ASP.NET基礎框架到達應用框架的過程,當UrlRoutingModule處理事後將RouteData對象封裝在RequestContext請求上下文中傳入到MvcHandler對象,而後MvcHandler對象經過IControllerFactory接口根據從RouteData中獲取到controllername控制器名稱字符串建立具體的IController對象實例;緩存

這基本的流程咱們是清晰了,可是咱們並不太清楚IControllerFactory背後所發生的一切,到底誰做爲IControllerFactory默認實現的,它又有着怎樣的擴展入口讓咱們來擴展建立過程,這值得一探究竟;框架

那麼這篇文章讓咱們來分析一下IControllerFactory的背後所發生的事情,咱們是否能從中學到什麼設計思想;ide

2】ASP.NETMVC IControllerFactory 控制器工廠接口

既然能將ControllerFactory提取出接口來,那麼對於IController的建立將是一個很是寬鬆的過程;簡單的設想一下,若是不將Factory提出接口來,那麼對於IController的建立將是一個很直觀的過程,可是ASP.NETMVC將IController建立不是簡單的使用一個ControllerFactory來解決,而是將這個建立過程設計的很鬆散,目的是爲了擴展性方便,換句話說咱們徹底能夠自定義一個Factroy來替代這個建立過程,也能夠基於系統內部的Factroy來擴展一下;模塊化

MvcHandler使用IControllerFactroy建立出相應IController對象,那麼首先咱們須要搞清楚MvcHandler經過什麼方式獲取到實現IControllerFactory接口的;函數

其實在MvcHandler中並非直接使用IControllerFactroy的相關實現,而是使用了ControllerBuilder對象,這個對象是一個單例模式的實現;MvcHanlder經過ControllerBuilder對象獲取到一個實例,而後經過ControllerBuilder建立出IControllerFactory實現;學習

 1 internal ControllerBuilder ControllerBuilder {
 2     get {
 3         if (_controllerBuilder == null) {
 4             _controllerBuilder = ControllerBuilder.Current;
 5         }
 6         return _controllerBuilder;
 7     }
 8     set {
 9         _controllerBuilder = value;
10     }
11 } 
12 
13 factory = ControllerBuilder.GetControllerFactory(); 

能夠簡單的理解爲,ControllerBuilder管理着IControllerFactory的建立過程,MvcHanlder經過獲取ControllerBuilder的全局實例,而後調用其方法GetControllerFactory,獲得能夠使用的IControllerFactory實現;ui

圖1:this

ControllerBuilder的設計很巧妙,它將IControllerFactory的實現爲咱們敞開了大門,咱們能夠經過這個入口作不少事情;

咱們看一下IControllerFactroy接口的定義:

1 public interface IControllerFactory {
2     IController CreateController(RequestContext requestContext, string controllerName);
3     SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
4     void ReleaseController(IController controller);
5 } 

接口中定義了三個方法,第一個方法CreateController很好理解,根據方法的第二個參數controllerName建立Controller實例;第二個方法GetControllerSessionBehavior方法是用來獲取controllerName所表明的Controller的Session行爲的,該行爲是經過SessionStateAttribute特性表示;第三個方法ReleaseController方法是用在最後釋放Controller的:

1 public virtual void ReleaseController(IController controller) {
2     IDisposable disposable = controller as IDisposable;
3     if (disposable != null) {
4         disposable.Dispose();
5     }
6 } 

因爲Controller繼承自IDisposable接口,因此在方法內部是直接調用Dispose方法來釋放資源;這裏須要注意的是,Controller對IDisposable接口的實現是virtual修飾符:

1 protected virtual void Dispose(bool disposing) {
2 } 

這就很方便咱們經過重寫此方法的方式來釋放一些其餘資源;

3】ASP.NETMVC DefaultControllerFactory 默認控制器工廠

在ASP.NETMVC內部有一個默認的Factroy(DefaultControllerFactroy),DefaultControllerFactroy實現了核心的建立IController代碼,這爲咱們的擴展提供了很好的接口;

經過調用IControllerFactory接口的CreateController(RequestContext requestContext, string controllerName) 方法,將進入到DefaultControllerFactory實現中,首要任務就是要根據controllerName名稱找到對應的ContorllerType,而後才能建立具體的實例;

 1 object routeNamespacesObj;
 2 Type match;
 3 if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
 4     IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
 5     if (routeNamespaces != null && routeNamespaces.Any()) {
 6         HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
 7         match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash); 
 8 
 9         // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
10         if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {
11             // got a match or the route requested we stop looking
12             return match;
13         }
14     }
15 }

首先根據請求的路由數據RouteData,查找設置的命名空間集合,而後使用命名空間和控制器名稱獲取Type,若是Type!=null而且沒有開啓後被命名空間則直接返回Type;

3.1】Controller中的AreaRegistration命名空間

在DefaultControllerFactroy內部使用到了兩組命名空間來做爲查找Controller的NameSpace,第一個是咱們在配置Route數據的時候設置的:

1 context.MapRoute(name: "api.order.default", url: "api/order/{controller}/{action}/{orderid}",
2     defaults: new { controller = "OrderController", action = "GetOrderOperationDatetime", orderid = "1001" },
3     namespaces: new string[] { "Api.Order" }); 

而第二個咱們通常都不會用它的,它是做爲AreaRegistration後備命名空間而存在的,是在ControllerBuilder中設置的:

1 ControllerBuilder.Current.DefaultNamespaces.Add("MvcApplication4.ApiOrder"); 

對後備命名空間的賦值是在AreaRegistrationContext中的MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) 方法中完成的:

1 if (namespaces == null && Namespaces != null) {
2      namespaces = Namespaces.ToArray();
3 } 

 

1 Route route = Routes.MapRoute(name, url, defaults, constraints, namespaces);
2 route.DataTokens["area"] = AreaName; 
3 
4 // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
5 // controllers belonging to other areas
6 bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);
7 route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback; 
8 
9 return route; 

因爲AreaRegistration可讓咱們對Controller的設計不侷限於ASP.NETMVCWeb程序中,而能夠將Controller獨立出去進行模塊化設計,因此須要提供有關Area的特殊命名空間查找方式;

4】ASP.NETMVC ControllerBuilder 控制器建立入口設置

ControllerBuilder做爲Controller建立的設置入口,能夠用來設置ControllerFactory替換系統默認的DefaultControllerFactory,ControllerBuilder是Controller的建立過程框架擴展入口,能夠藉助ControllerBuilder方便作不少設置;

1 internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) {
2     _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
3         () => _factoryThunk(),
4          new DefaultControllerFactory { ControllerBuilder = this },
5         "ControllerBuilder.GetControllerFactory"
6     );
7 } 

在ControllerBuilder的構造函數中,初始化了一個SingleServiceResolver<IControllerFactory>類型的Resolver,目的是爲了對Factory實現IOC方式的獲取;在代碼中,實例化了一個DefaultControllerFactory類型的實例做爲默認的Factory,比較重要的是將ControllerBuilder作爲參數設置到了ControllerBuilder屬性中,目的是爲了能在後面解析Controller命名空間的時候用到;

1 public HashSet<string> DefaultNamespaces {
2     get {
3         return _namespaces;
4     }
5 }

在此咱們能夠設置統一的命名空間,因爲咱們在設置Route的時候,都須要設置namesapce字段,可是若是有不少這樣的Route的時候就很麻煩,咱們能夠經過此方式進行統一的設置;

1 public void SetControllerFactory(IControllerFactory controllerFactory) {
2     if (controllerFactory == null) {
3         throw new ArgumentNullException("controllerFactory");
4     } 
5 
6     _factoryThunk = () => controllerFactory;
7 }

還有一個比較重要的就是設置自定義的ControllerFactory,在方法SetControllerFactory中,咱們能夠設置一個IControllerFactory類型的對象,就能夠接管系統默認的DefaultControllerFactory對象,包括後面的全部的IController緩存策略;

圖2:

基本上咱們能夠經過ControllerBuilder進入到ControllerFactroy的建立環節來,使用SetControllerFactory方法直接將咱們自定義的IControllerFactroy傳入便可;

5】ASP.NETMVC 自定義IControllerFactory

既然知道了ContollerBulder能夠使咱們更改系統默認的控制器工廠,那麼咱們經過怎樣的方式使用如今的Factroy;大體上咱們只須要繼承自DefaultControllerFactory而後進行相應的擴展便可;

1 public class CustomControllerFactory : DefaultControllerFactory
2 {
3     protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
4     {
5         Console.WriteLine(string.Format("{0}is create.", controllerType.Name));
6         return base.GetControllerInstance(requestContext, controllerType);
7     }
8 }

如今假設咱們須要在系統建立全部Controller的時候能記錄下建立的記錄信息,這樣就很方便的完成了,咱們只須要在系統初始化的地方進行設置:

1 ControllerBuilder.Current.SetControllerFactory(new Api.Order.CustomControllerFactory());

這樣咱們就接管了ControllerFactory的部分功能;

 

相關文章
相關標籤/搜索