MVC的DependencyResolver组件

一、前言

  DependencyResolver是MVC中一个重要的组件,从名字可以看出,它负责依赖对象的解析,可以说它是MVC框架内部使用的一个IOC容器。MVC内部很多对象的创建都是通过它完成的,或许我们平时没有直接用到它,但是如果你在使用unity、autofac,或者在看一些开源项目时,总会看到它的身影。接下来就让我们看一下这个组件是如何工作的。

二、通过Controller的激活理解DependencyResolver的工作过程

  这里先插一个题外话,经常会有面试问:asp.net 几个核心对象是什么?一般人都会回答:Server、Request、Response、Session、Cookie这些。但我的回答会是HttpApplication、HttpHandler和HttpModule,这才是管道模型中的核心类型,整个asp.net的处理流程和可扩展性也都是建立在这几个对象上的。

  回到主题,asp.net请求都是交给HttpHandler处理的,对于MVC来说,是交给一个MvcHandler,它负责激活Controller,如果你不知道为什么,请看这里。在这里我们直接定位到MvcHandler的PR方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
    IController controller;
    IControllerFactory factory;
    ProcessRequestInit(httpContext, out controller, out factory);
 
    //其它操作
    //调用 controller.Execute方法
}
 
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
    HttpContext currentContext = HttpContext.Current;
 
    //从路由获取controller名称
    string controllerName = RequestContext.RouteData.GetRequiredString("controller");
 
    //通过ControllerBuilder获取ControllerFactory,默认就是DefaultControllerFactory
    factory = ControllerBuilder.GetControllerFactory();
 
    //通过ControllerFactory获取Controller对象
    controller = factory.CreateController(RequestContext, controllerName);
}

  ControllerFactory故名思议就是用于创建Controller的,我们也可以自己实现IControllerFactory,参与Controller的激活过程,具体是在全局调用ControllerBuilder.Current.SetControllerFactory方法。我们这里主要关注的是Controller的激活过程,实际上它们的创建过程是相似的。默认使用的ControllerFactory是DefaultControllerFactory。DefaultControllerFactory的CreateController方法如下:  

1
2
3
4
5
6
7
8
9
10
11
12
13
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
    //获取Controller类型
    Type controllerType = GetControllerType(requestContext, controllerName);
 
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
}
 
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    return ControllerActivator.Create(requestContext, controllerType);
}

  可以看到,它通过一个ControllerActivator来创建IController对象,默认使用的是DefaultControllerActivator。与ControllerFactory类似,我们可以实现IControllerActivator,参与Controller的激活过程,具体是将ControllerActivator作为DefaultConrtollerFactory构造函数参数,然后再在全局调用ControllerBuilder.Current.SetControllerFactory方法。可以看到MVC的Controller激活过程是很灵活的,它提供多种方式让我们自定义激活过程。DefaultControllerActivator定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private class DefaultControllerActivator : IControllerActivator
{
    private Func<IDependencyResolver> _resolverThunk;
 
    public DefaultControllerActivator()
        this(null)
    {
    }
 
    public DefaultControllerActivator(IDependencyResolver resolver)
    {
        if (resolver == null)
        {
            _resolverThunk = () => DependencyResolver.Current;
        }
        else
        {
            _resolverThunk = () => resolver;
        }
    }
 
    public IController Create(RequestContext requestContext, Type controllerType)
    {
        try
        {
            return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
        }
        catch (Exception ex)
        {
        }
    }
}

  这里的_resolverThunk是一个用于获取IDepencyResolver对象的委托,实际获得的是DependencyResolver.Current。我们也可以自己实现IDependencyResolver,参与Controller的激活过程,具体是在全局调用DependencyResolver的静态方法SetResolver方法。需要注意的是这里的DependencyResolver类型(这里是类型,而其它地方提到的DependencyResolver都是组件的意思)并没有实现IDependencyResolver接口,我觉得将它命名为DependencyResolverContainer会更合适一些。IDepdencyResolver接口的定义如下:

1
2
3
4
5
public interface IDependencyResolver
{
    object GetService(Type serviceType);
    IEnumerable<object> GetServices(Type serviceType);
}

  默认DependencyResolver.Current使用的是DefaultDependencyResolver类型,这里又和ControllerFactory和ControllerActivator的设计一样了,如果我们自定义,那么就使用,否则就使用默认的。DefaultDependencyResolver定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private class DefaultDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if (serviceType.IsInterface || serviceType.IsAbstract)
        {
            return null;
        }
 
        try
        {
            //如果Controller Type创建Controller实例对象
            return Activator.CreateInstance(serviceType);
        }
        catch
        {
            return null;
        }
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return Enumerable.Empty<object>();
    }
}

  可以看到,MVC会将Controller对象的创建通过DependencyResolver完成。将对象的创建通过DependencyResolver完成的好处是可以降低对象间的耦合度;另外,通过实现IDependencyResolver接口,我们可以完全控制对象的创建过程,例如将对象的依赖关系转移到配置文件中等等。

  通过上面我们还知道了有三种默认类型:DefaultControllerFactory、DefaultControllerActivator和DefaultDependencyResolver,分别对应三个接口:IControllerFactory、IControllerActivator、IDependencyResolver。它们的设计是类似的,都是提供给外部一个接口,如果外部自己实现了这个过程,那么就使用,否则用默认的。实际上这也是我们参与Controller激活过程的三种做法。

三、实现IDependencyResolver接口

  接下来通过一个例子证明上面的过程。我们要实现的需求是通过实现IDependencyResolver接口,实现Controller构造函数注入服务。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HomeController : Controller
{           
    private IUserService _service;
    public HomeController(IUserService service)
    {
        _service = service;
    }
 
    public ActionResult Index()
    {
        return Content(_service.GetUserName());
    }
}

  HomeController只依赖于IUserService接口,不依赖于具体对象。

  接下来我们实现IDependencyResolver接口,依赖注入的实现方式有很多种,这里我们使用Unity。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UnityDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if(serviceType == null)
        {
            throw new ArgumentNullException("serviceType");
        }           
        return (serviceType.IsClass && !serviceType.IsAbstract)
            || Ioc.IsRegistered(serviceType) ? Ioc.GetService(serviceType) : null;
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (serviceType == null)
        {
            throw new ArgumentNullException("serviceType");
        }
        return (serviceType.IsClass && !serviceType.IsAbstract)
            || Ioc.IsRegistered(serviceType) ? Ioc.GetServices(serviceType) : null;
    }
}

  这里需要判断 (serviceType.IsClass && !serviceType.IsAbstract) || Ioc.IsRegistered(serviceType) 原因是我们前面说过的,MVC内部很多对象都是通过DependencyResolver组件创建的,如上面的IConrtollerFactoy,所以这里我们只负责对已注册的类型或类(非抽象类)进行解析。

  Ioc类在这里很简单,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Ioc
{
    private static IUnityContainer _container = new UnityContainer();
 
    public static void RegisterType<TFrom,TTo>()
        where TTo : TFrom
    {           
        _container.RegisterType<TFrom, TTo>();
    }
 
    public static object GetService(Type type)
    {                       
        return _container.Resolve(type);
    }
 
    public static IEnumerable<object> GetServices(Type type)
    {
        return _container.ResolveAll(type);
    }
 
    public static bool IsRegistered(Type type)
    {
        return _container.IsRegistered(type);
    }
}

  接着,在Application_Start方法中,注册Service和设置IocDependencyResolver:

1
2
Ioc.RegisterType<IUserService, UserService>();
DependencyResolver.SetResolver(new IocDependencyResolver());

  运行就可以看到HomeController构造函数的IUserService就是UserService类型了。

四、总结

  实际上,上面的例子我们也可以用实现IControllerFactory或者IControllerActivator达到同样的目的,但使用IDependencyResolver会更简单一点,而且大部分的IOC框架都已经提供了这样的功能。例如上面UnityDependencyResolver根本不用自己定义,Unity for MVC 已经有这么一个类型了,直接使用即可。如果使用Autofac的话可以是:DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

MVC的DependencyResolver组件的更多相关文章

  1. 理解ASP.NET MVC的DependencyResolver组件

    一.前言 DependencyResolver是MVC中一个重要的组件,从名字可以看出,它负责依赖对象的解析,可以说它是MVC框架内部使用的一个IOC容器.MVC内部很多对象的创建都是通过它完成的,或 ...

  2. ASP.NET Core MVC – Tag Helper 组件

    ASP.NET Core Tag Helpers系列目录,这是第五篇,共五篇: ASP.NET Core MVC – Tag Helpers 介绍 ASP.NET Core MVC – Caching ...

  3. asp.net mvc 模型验证组件——FluentValidation

    asp.net mvc 模型验证组件——FluentValidation 示例 using FluentValidation; public class CustomerValidator: Abst ...

  4. asp.net MVC通用分页组件 使用方便 通用性强

    asp.net MVC通用分页组件 使用方便 通用性强   该分页控件的显示逻辑: 1 当前页面反色突出显示,链接不可点击 2 第一页时首页链接不可点击 3 最后一页时尾页链接不可点击 4 当前页面左 ...

  5. Spring核心原理分析之MVC九大组件(1)

    本文节选自<Spring 5核心原理> 1 什么是Spring MVC Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 S ...

  6. ASP.NET MVC 模块与组件(一)——发送邮件

    我的见解: 模块化与组件化是编程的一种思想:提高代码的重用性,提高开发效率. 常见的模块化就是函数与各种类型的封装,若是代码具有更高的重用价值(能够提供给别人使用),建议可以考虑封装成动态链接库(dl ...

  7. MVC值提供组件ValueProvider的继承关系

    MVC请求过程中中各组件调用顺序:值提供组件(IValueProvider)->模型绑定组件(IModelBinder)->模型验证组件 值提供组件接口 public interface ...

  8. MVC 路由检测组件 Routing Debugger

    组件下载地址 haacked.com 1.在mvc项目中引入组件 2.配置route规则 public static void RegisterRoutes(RouteCollection route ...

  9. ASP.NET Core MVC 之视图组件(View Component)

    1.视图组件介绍 视图组件是 ASP.NET Core MVC 的新特性,类似于局部视图,但它更强大.视图组件不使用模型绑定,并且仅依赖于调用它时所提供的数据. 视图组件特点: 呈块状,而不是整个响应 ...

随机推荐

  1. System Request 进入KDB模式过程详解

    0   echo g > /proc/sysrq-trigger   怎么让系统停下来,进入进入KDB循环? 1   需要简单了解下:Linux Magic System Request 2   ...

  2. Swift代码实现加载WEBVIEW

    let webview = UIWebView(frame:self.view.bounds) webview.bounds=self.view.bounds //远程网页 webview.loadR ...

  3. TPL异步并行编程之简单使用

    并行编程一直是一个老生常谈的话题 在这里记录一下TPL编程,这在net4.0 微软就已经提供了多核时代下的并行库,其中最核心的最常用的也就是Task 一 Task是什么 Task可以简单的理解为一个线 ...

  4. java entry

    我希望要一个ArrayList<Entry>,类似C++中的pair, 可是Map.Entry是个接口,不能实例化,能够像以下这样写 HashMap<Integer, Integer ...

  5. Swift语言Auto Layout入门教程:上篇

    原文:Beginning Auto Layout Tutorial in Swift: Part 1/2,译者:@TurtleFromMars 开始用自动布局约束的方式思考吧! 更新记录:该教程由Br ...

  6. 《c陷阱与缺陷》笔记--移位运算

    #include <stdio.h> int main(void){ int a = 2; a >> 32; a >> -1; a << 32; a & ...

  7. boost库在工作(33)网络服务端之三

    在这个例子里,表示服务器与一个客户端的沟通渠道,就是一个连接,封装为类CConnect.它是当服务器接收到一个客户端连接请求之后创建的,主要用来就是管理这个连接的生命周期,以及数据的接收和发送.从生命 ...

  8. 关于PHP的内置服务器的使用

    今天刚开始正式学习PHP(之前有一点了解),推荐学习的网站是w3school.一开始不知道tomcat服务器不支持PHP脚本,直接把.php文件放到tomcat里面去运行,结果嵌入的php代码段没有什 ...

  9. 14.3.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁

    14.3.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁 这个章节建立关于死锁的概念信息,它解释如何组织数据库操作来减少死锁和随后的错误处理: D ...

  10. http调试工具Charles Proxy用法详解

    Charles Proxy 通常称为Charles,Charles是目前最强大的http调试工具,在界面和功能上远强于Fiddler,同时是全平台支持,堪称圣杯级工具,不过在这里为您提供了Charle ...