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
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 =this.ControllerActivator.Create(requestContext, controllerType);
}
CreateController通过GetControllerType方法来获取controllerName指定的控制器类型名称,并通过
GetControllerInstance方法中一个this.ControllerActivator.Create方法通过依赖注入的方式来创建当前controllerType指定的IController对象(默认情况下ControllerActivator将调用DefaultControllerActivator控制激活器),DefaultControllerActivator 源代码如下
private class DefaultControllerActivator : IControllerActivator
{
// Fields
private Func<IDependencyResolver> _resolverThunk;
// Methods
public DefaultControllerActivator() : this(null)
{
}
public DefaultControllerActivator(IDependencyResolver resolver)
{
Func<IDependencyResolver> func = null;
if (resolver == null)
{
this._resolverThunk = () => DependencyResolver.Current;
}
else
{
if (func == null)
{
func = () => resolver;
}
this._resolverThunk = func;
}
}
public IController Create(RequestContext requestContext, Type controllerType)
{
IController controller;
try
{
controller = (IController) (this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception exception)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[] { controllerType }), exception);
}
return controller;
}
}
DefaultControllerActivator包含有两个构造函数,如果构造函数参数中IDependencyResolver实例为空(即没有使用自定义DI容器),则this._resolverThunk默认将使用DependencyResolver
组件中的DI容器
注意:这里的resolverThunk是指一个获取IDependencyResolver对象的委托,获取的是实现了IDependencyResolver接口的实例对象,但是System.Web.MVC.DependencyResolver组件并没有实现IDependencyResolver接口,只是用于对DI容器进行装载,所以需要通过调用DependencyResolver.current属性加载DI容器
//DependencyResolver构造函数
public DependencyResolver()
{
this.InnerSetResolver((IDependencyResolver) new DefaultDependencyResolver());
}
//InnerSetResolver方法的具体实现
public void InnerSetResolver(IDependencyResolver resolver)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}
this._current = resolver;
this._currentCache = new CacheDependencyResolver(this._current);
}
如果我们查看DependencyResolver类的构造函数可以发现,在DependencyResolver将默认启用MVC框架中的DI容器并赋值给_current属性(即DefaultDependencyResolver),但是我们也可以通过SetResolver方法在全局中创建新的DI容器对DefaultDependencyResolver进行替换,例如:我们创建AutofacDependencyResolverDI容器对DefaultDependencyResolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
//SetResolver内部中调用InnerSetResolver方法添加DI容器,从而实现对DefaultDependencyResolver的替换
public static void SetResolver(IDependencyResolver resolver)
{
_instance.InnerSetResolver(resolver);
}
同时在Create方法中,首先将通过GetService(controllerType)方法来获取controllerType指定的对象类型,如果获取的类型为空,则将直接通过Activator.CreateInstance方法来创建controllerType对象实例。
result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
我们返回到DefaultDependencyResolver类型(默认的DI容器)以及AutofacDependencyResolver类型中,来看一下GetService方法的具体实现;
public object GetService(Type serviceType)
{
if (serviceType.IsInterface || serviceType.IsAbstract)
{
return null;
}
try
{
return Activator.CreateInstance(serviceType);
}
catch
{
return null;
}
}
添加依赖注入即可以实现单一添加,也可以实现批量添加,例如:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();
builder.RegisterAssemblyTypes(typeof(CartRespository).Assembly).Where(t => t.Name.EndsWith("Respository")).AsImplementedInterfaces();
builder.RegisterType().As()方法为UnitOfWork类型添加了依赖注入,然后通过DependencyResolver.GetService方法获取UnitOfWork类型的实例对象,在创建实例对象的过程中,DependencyResolver将对UnitOfWork的构造函数进行扫描,如果UnitOfWork的构造函数中包含具有依赖关系的参数,则将自动为该参数创建:
通过依赖注入来创建实例的方式主要包含三种:(1)构造器注入;(2)属性注入;(3)接口注入。所以需要实现完全的关注分离,我们可以将Controller对象构造函数进行重构,将需要通过依赖注入创建的interface作为参数添加到构造函数的参数当中;