剖析ASP.NET 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方法:
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方法如下:
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定义如下:
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接口的定义如下:
public interface idependencyresolver { object getservice(type servicetype); ienumerable<object> getservices(type servicetype); }
默认dependencyresolver.current使用的是defaultdependencyresolver类型,这里又和controllerfactory和controlleractivator的设计一样了,如果我们自定义,那么就使用,否则就使用默认的。defaultdependencyresolver定义如下:
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构造函数注入服务。如:
public class homecontroller : controller { private iuserservice _service; public homecontroller(iuserservice service) { _service = service; } public actionresult index() { return content(_service.getusername()); } }
homecontroller只依赖于iuserservice接口,不依赖于具体对象。
接下来我们实现idependencyresolver接口,依赖注入的实现方式有很多种,这里我们使用unity。如下:
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类在这里很简单,如下:
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:
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));
以上就是本文的全部内容,希望对大家的学习有所帮助。
推荐阅读
-
剖析ASP.NET MVC的DependencyResolver组件
-
asp.net MVC利用ActionFilterAttribute过滤关键字的方法
-
ASP.NET中MVC传递数据的几种形式总结
-
ASP.NET MVC的四种验证编程方式
-
ASP.NET MVC使用ActionFilterAttribute实现权限限制的方法(附demo源码下载)
-
asp.net MVC利用自定义ModelBinder过滤关键字的方法(附demo源码下载)
-
ASP.NET MVC @Helper辅助方法和@functons自定义函数的使用方法
-
ASP.NET MVC中图表控件的使用方法
-
ASP.NET MVC 3仿Server.Transfer效果的实现方法
-
详解Asp.Net MVC的Bundle捆绑