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. badi增强

    对于根据事务代码查找对应的BADI,网上介绍的方法很多,但总结下来无非就两种方法,在此把它记录下来,方便以后自己查阅了. (1)通过SE24,输入CL_EXITHANDLER,然后在方法GET_INS ...

  2. Lucene.Net 2.3.1开发介绍 —— 二、分词(一)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...

  3. 【PHP SDK for OpenStack/Rackspace APIs】身份验证

    在你使用php-opencloud之前必须先取得云服务提供商的身份验证.这是整个过程中最简单也是最让人沮丧的部分. 说它最简单是因为你只需要两部分信息: 云服务提供商的身份验证端点 用来身份验证的证书 ...

  4. 让Android中的webview支持页面中的文件上传

    android webview在默认情况下是不支持网页中的文件上传功能的: 如果在网页中有<input type="file" />,在android webview中 ...

  5. (step6.3.2)hdu 1068(Girls and Boys——二分图的最大独立集)

    题目大意:第一行输入一个整数n,表示有n个节点.在接下来的n行中,每行的输入数据的格式是: 1: (2) 4 6 :表示编号为1的人认识2个人,他们分别是4.6: 求,最多能找到多少个人,他们互不认识 ...

  6. 在phpmyadmin后台获取webshell方法汇总整理

    方法一: CREATE TABLE `mysql`.`xiaoma` (`xiaoma1` TEXT NOT NULL ); INSERT INTO `mysql`.`xiaoma` (`xiaoma ...

  7. CodeForces Round #191 (327C) - Magic Five 等比数列求和的快速幂取模

    很久以前做过此类问题..就因为太久了..这题想了很久想不出..卡在推出等比的求和公式,有除法运算,无法快速幂取模... 看到了 http://blog.csdn.net/yangshuolll/art ...

  8. [C#基础] 委托

    什么是委托 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有 ...

  9. How to append files to a .tar archive using Apache Commons Compress?(转)

    I created a copy of the tar archive and copied to entire content to it. Then I delete the old tar ar ...

  10. Spark&amp;Spark性能调优实战

    Spark特别适用于多次操作特定的数据,分mem-only和mem & disk.当中mem-only:效率高,但占用大量的内存,成本非常高;mem & disk:内存用完后,会自己主 ...