阅读目录:html
上一篇文章“.NET/ASP.NET MVC Controller 控制器(一:深刻解析控制器运行原理)”详细的讲解了MvcHandler对象内部的基本流程逻辑,这基本的流程逻辑为咱们后面的学习起到铺垫做用,当咱们能正确的搞懂它的内部执行流程后,咱们就能够顺藤摸瓜的去挖掘每一个逻辑环节中的详细逻辑;api
经过前面两篇文章的介绍,咱们基本上能搞清楚一个Url请求是如何借助于UrlRoutingModule模块顺利穿过ASP.NET基础框架到达应用框架的过程,当UrlRoutingModule处理事后将RouteData对象封装在RequestContext请求上下文中传入到MvcHandler对象,而后MvcHandler对象经过IControllerFactory接口根据从RouteData中获取到controllername控制器名称字符串建立具体的IController对象实例;缓存
这基本的流程咱们是清晰了,可是咱们并不太清楚IControllerFactory背后所发生的一切,到底谁做为IControllerFactory默认实现的,它又有着怎样的扩展入口让咱们来扩展建立过程,这值得一探究竟;框架
那么这篇文章让咱们来分析一下IControllerFactory的背后所发生的事情,咱们是否能从中学到什么设计思想;ide
既然能将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 }
这就很方便咱们经过重写此方法的方式来释放一些其余资源;
在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;
在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的特殊命名空间查找方式;
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传入便可;
既然知道了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的部分功能;
做者:王清培
出处:http://www.cnblogs.com/wangiqngpei557/
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。