阅读目录:

  • 1.开篇介绍
  • 2.ASP.NETMVC IControllerFactory 控制器工厂接口
  • 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂
  • 4.ASP.NETMVC ControllerBuilder 控制器创建入口设置
  • 5.ASP.NETMVC 自定义IControllerFactory

1】开篇介绍

上一篇文章“.NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)”详细的讲解了MvcHandler对象内部的基本流程逻辑,这基本的流程逻辑为我们后面的学习起到铺垫作用,当我们能正确的搞懂它的内部执行流程后,我们就可以顺藤摸瓜的去挖掘每个逻辑环节中的详细逻辑;

通过前面两篇文章的介绍,我们基本上能搞清楚一个Url请求是如何借助于UrlRoutingModule模块顺利穿过ASP.NET基础框架到达应用框架的过程,当UrlRoutingModule处理过后将RouteData对象封装在RequestContext请求上下文中传入到MvcHandler对象,然后MvcHandler对象通过IControllerFactory接口根据从RouteData中获取到controllername控制器名称字符串创建具体的IController对象实例;

这基本的流程我们是清晰了,但是我们并不太清楚IControllerFactory背后所发生的一切,到底谁作为IControllerFactory默认实现的,它又有着怎样的扩展入口让我们来扩展创建过程,这值得一探究竟;

那么这篇文章让我们来分析一下IControllerFactory的背后所发生的事情,我们是否能从中学到什么设计思想;

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实现;

图1:

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的部分功能;

作者:王清培

出处:http://www.cnblogs.com/wangiqngpei557/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

.NET/ASP.NET MVC Controller 控制器(IController控制器的创建过程)相关推荐

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

    Controller激活系统最终通过注册的ControllerFactory创建相应的Conroller对象,如果没有对ControllerFactory类型或者类型进行显式注册(通过调用当前Cont ...

  2. asp.net mvc 使用ajax请求 控制器 (PartialViewResult)分部的action,得到一个分部视图(PartialView)的HTML,进行渲染...

    在asp.net mvc 使用ajax请求获取数据的时候,我们一般是返回json或者xml,然后解析这些数据进行渲染,这样会比较麻烦,可以请求一个 分部action,返回一个分部视图 直接可以渲染,不 ...

  3. ASP.NET MVC 音乐商店 - 2.控制器

    在典型的 Web 应用中,用户请求的 URL 地址通常映射到保存在网站中的文件上,例如,当用户请求 /Products.aspx 的时候,或者 /Products.php 的时候,很可能是在通过处理 ...

  4. ASP.NET MVC Controller Overview摘录

    原文URL:http://www.asp.net/learn/mvc/tutorial-03-cs.aspx Understanding Controllers 1. Controller定义   M ...

  5. (转)[翻译] ASP.NET MVC Tip #1 - 使用扩展方法创建新的HTML Helper

    原文地址:http://weblogs.asp.net/stephenwalther/archive/2008/06/13/asp-net-mvc-tip-1-creating-new-html-he ...

  6. Asp.net MVC中如何获取控制器的名称

    如果在代码中 当前controller.action的获取 RouteData.Route.GetRouteData(this.HttpContext).Values["controller ...

  7. 在Asp.net MVC framework中使用扩展方法创建Html Helper

    HtmlHelper提供了一些帮助的方法返回一个字符串来生成html. 在System.Web.Mvc.Html命称空间下有一些表单,控件,局部视图Helper方法.我将创建一个生成标签<inp ...

  8. ASP.NET MVC: 构建不带 Web 窗体的 Web 应用程序(转载)

    我 从事专业开发迄今为止已有 15 年,在此之前,我利用业余时间从事开发至少也有 10 年了.与我这一代的大多数人一样,我是从 8 位计算机起步,然后转用 PC 平台的.随着计算机的复杂性日益增加,我 ...

  9. ASP .NET MVC 架构

    ASP.NET MVC 架构 我在写上位软件的时候基本不用MVC架构,MVC架构的分层思想是值得借鉴的.一般我采用如下架构: 1 UI 表示层,处理与用户的交互,获取显示用户数据. 2 BLL 业务逻 ...

最新文章

  1. 多分类loss函数本质理解
  2. QPushButton 点击信号分析
  3. Asp.Net生命周期系列一
  4. 网页中嵌入Excel控件
  5. 推荐一个提供全球新冠肺炎确诊,死亡和治愈人数的网站,提供csv下载
  6. mysql 8小时问题_Mysql经典的“8小时问题”
  7. 做移动端视频通话软件,大致看了下现有的开源软件(转)
  8. Spring+Hibernate+SpringMVC+MySql实现配置多个数据源!
  9. linux修改端口cost值,Linux下通过修改网卡驱动的参数调整Intel网卡的性能
  10. IntelliJ IDEA 创建Java Web项目
  11. openstack版本历史
  12. “页面制作人员”?“页面工程师”?“页面架构师”?滚一边去!
  13. ehcache 一二事 - ssm 中ehcashe的简单配置应用
  14. 爬虫第十一式:用selenium爬取民政部行政区划代码
  15. wps垂直排列标题与文本_如何垂直设置wps文本
  16. 首先,打破一切常规 学习笔记 之五
  17. 深入java虚拟机视频教程_从原理到实战深入学习JAVA虚拟机,视频教程下载
  18. mysql根据一个表的字段更新另一个表的字段
  19. 【有利可图网】PS教程:用滤镜打造3D立体文字效果
  20. 《无名之辈》小人物的自我证明

热门文章

  1. 代码同步工具_可以多重连接的数据库管理工具
  2. linux文件 内存映射 锁,linux – mmap:将映射文件立即加载到内存中吗?
  3. 初三计算机会考成都,高中阶段教育学校统一招生考试试卷(含成都市初三毕业会考)...
  4. pcb过孔漏铜_为什么PCB板在生产中会铜线脱落?
  5. object 构造器java_“java”中为什么“Object”类要有一个空的构造函数?
  6. python xlrd xlwt pandas 模块 区别_python如何读写excel文件|python教程|python入门|python教程...
  7. pro调用python libs_使用WingPro 7 设置Python路径的方法
  8. 什么是对象的消息_SpringBoot+RabbitMQ方式收发消息,一文带你体验
  9. jieba 分词的三种模式
  10. mysqldump导出数据库 (dos环境下)