阅读目录:

  • 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 = "" },
  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 == );
  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 控制器(二:IController控制器的创建过程)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4 ...

  2. ASP.NET MVC中使用异步控制器

    线程池 一直想把项目改写成异步,但是ASP.NETMVC3下写的过于繁琐,.NET 4.5与ASP.NET MVC下代码写起来就比较简单了, MS好像也一直喜欢这样搞,每一个成熟的东西,都要演变好几个 ...

  3. ABP示例程序-使用AngularJs,ASP.NET MVC,Web API和EntityFramework创建N层的单页面Web应用

    本片文章翻译自ABP在CodeProject上的一个简单示例程序,网站上的程序是用ABP之前的版本创建的,模板创建界面及工程文档有所改变,本文基于最新的模板创建.通过这个简单的示例可以对ABP有个更深 ...

  4. 转载——Asp.Net MVC+EF+三层架构的完整搭建过程

    转载http://www.cnblogs.com/zzqvq/p/5816091.html Asp.Net MVC+EF+三层架构的完整搭建过程 架构图: 使用的数据库: 一张公司的员工信息表,测试数 ...

  5. (转)ASP.NET Mvc 2.0 - 1. Areas的创建与执行

    转自:http://www.cnblogs.com/terrysun/archive/2010/04/13/1711218.html ASP.NET Mvc 2.0 - 1. Areas的创建与执行 ...

  6. .NET/ASP.NET MVC Controller 控制器(深入解析控制器运行原理)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程) 3.ASP.NETMVC Controller 控制器的入口(Controll ...

  7. 二、ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程) 3.ASP.NETMVC Controller 控制器的入口(Controll ...

  8. NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)

    阅读目录: 1.开篇介绍 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程) 3.ASP.NETMVC Controller 控制器的入口(Controll ...

  9. asp.net mvc Controller控制器返回类型

    ASP.NET MVC包括了执行常见任务的ActionResult类型.这些类型罗列在表5-1中.每个类型都将在随后的小节中详细讨论. 表5-1  动作结果的类型及其说明 动作结果的类型 说    明 ...

随机推荐

  1. Node.js入门初体验

    今天有一个类似网络爬虫的需求,本来打算用我还算熟悉的asp或者asp.NET来做这个事情,但是写了这么长时间js,asp的语法实在不喜欢,VS又早被我卸掉了,思来想去打算用一下最近比较火的Node.j ...

  2. Mongo查询

    这里主要是讲MongoDB在控制台中如何进行高级查询. 还有一句想提醒大家,多动手实验,才是硬道理. <,>,>=,<= 这四个就不用解释了,最常用的,也是最简单的. db.c ...

  3. Extjs4.0以上版本智能提示的方法

    最近,公司的BS项目要用Extjs,本屌学过JavaScript..和Jquery Easy UI  ,Jquery..可这个Extjs完全没接触过..可公司项目进度不能掉..只有苦心学习,终于写了点 ...

  4. 常用的WebForm 控件

    首先回忆一下Html页中的12个表单元素 .文本类 文本框 <input type="text" id="" name="" valu ...

  5. c# asp.net4.0尚未在web服务器上注册

    辗转无数论坛,感谢http://bbs.csdn.net/topics/390992746 microsoft官方文档: https://blogs.msdn.microsoft.com/webdev ...

  6. Git 获取文件操作

    1. 在本地新建存放源代码的文件夹: 2. 进入文件夹下,右击选择 Git Bash Here ,弹出git命令行窗口: 3. 运行指定 git init,初始化git: 4. git  remote ...

  7. GridView嵌套在ScrollView里只有一行的问题

    遇到这个问题 网上找到的解决办法: 方法一:就是上面说的通过计算出来ListView或者GridView中的子列高度和 进行显示:public void setListViewHeightBasedO ...

  8. node-inspector调试工具应用

    一.起因 想调试某些开源的nodejs项目,然后就选择了node-inspector插件. 他的优点: 1.可以借用chrome或firefox浏览器进行调试,与前端调试融合. 2.配置简单 二.必备 ...

  9. 熟悉vs2012IDE

    使用vs2012已经几个月了,深感对开发环境的学习有助于提高开发的效率.现将我的经验总结如下: 一.搜索 vs2012相比vs2010添加了正则搜索,极大的提高了代码的查询效率. 二.重构 同vs20 ...

  10. 轻松掌握:JavaScript策略模式

    策略模式 定义:定义一系列的算法,把它们一个个封装成函数,也可把它们作为属性统一封装进一个对象,然后再定义一个方法,该方法可根据参数自动选择执行对应的算法. 一般用于在实现一个功能时,有很多个方案可选 ...