一. 为什么要进行扩展
在前面的分析中,我们知道默认的Controller激活系统只能实例化无参构造函数的Controller类型,但在某些情况一下,我们希望某些服务的实例能够自动注入到Controller实例中,从而达到服务接口和实现的隔离,减小重复的代码,提高系统的可维护性和灵活性。也就是说我们希望在Controller激活中引入依赖注入。关于依赖注入的概念这里就不解释了,请自行查询相关的资料。基于.net依赖注入框架也有很多,下面的例子主要使用微软企业库的Unity。
在上一节的分析中,我们知道Controller的激活实际是包括获取IControllerFacotry和IController实例, 在这两个级别都可以引入扩展实现在我们的目的,现在具体来看一下。
一.实列化IControllerFactory Level
在这个Level我们知道了路由系统提供的路由信息,当然甚至可以从头到尾自定义实现一个ControllerFactory,包括Controller类型的确定和实例化,但通常没什么必要。我们目标是在Controller实例化阶段注入服务实例,主要还是在Controller实例化阶段。
二. 实列化Controller Level
在这一阶段,我们已经确定Controller的类型,通过上一节的分析,有三个点我们可以引入依赖注入。我们假设有一个要显示所有Customer信息的页面,在其中应用了仓储模式,在CustomerController实例化进自动注入仓储的实例。
1. 继承DefaultControllerFactory,重写GetControllerInstance方法。大概的代码如下:
public class UnityControllerFactory : DefaultControllerFactory {
public IUnityContainer Container
{
get;
private set;
} public UnityControllerFactory(IUnityContainer container)
{
this.Container = container;
} protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (IController)this.Container.Resolve(controllerType);
}
}
然后在Global.asax的Application_Start中注册
private void RegisterUnityControllerFactory() {
IUnityContainer container = new UnityContainer();
container.RegisterType<ICustomerRepository, CustomerRepository>();
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
2. 自定义实现IControllerActivator。大概的代码如下:
public class UnityControllerActivator : IControllerActivator {
public IUnityContainer Container
{
get;
private set;
} public UnityControllerActivator(IUnityContainer c)
{
this.Container = c;
} public IController Create(RequestContext requestContext, Type controllerType)
{
return (IController)this.Container.Resolve(controllerType);
}
}
注册代码:
private void RegisterUnityControllerActivator() {
IUnityContainer container = new UnityContainer();
container.RegisterType<ICustomerRepository, CustomerRepository>();
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new UnityControllerActivator(container)));
}
3.自定义实现全局的IDependencyResolver
public class UnityDependencyResolver : IDependencyResolver
{
private IUnityContainer Container
{
get;
set;
} public UnityDependencyResolver()
{
this.Container = new UnityContainer();
} public UnityDependencyResolver RegisterType<TFrom, TTo>() where TTo : TFrom
{
this.Container.RegisterType<TFrom, TTo>();
return this;
} public object GetService(Type serviceType)
{
try
{
return this.Container.Resolve(serviceType);
}
catch(ResolutionFailedException)
{
return null;
}
} public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return this.Container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
注册代码:
private void SetDependencyResolver()
{
UnityDependencyResolver resolver = new UnityDependencyResolver();
resolver.RegisterType<ICustomerRepository, CustomerRepository>();
DependencyResolver.SetResolver(resolver);
}
三.真的要扩展吗?
天下没有完美的东西,引入一样东西我们必须要充分认识它的优势,也要认清它的副作用。在实际项目中,个人觉得大部分情况是没必要在Controller这个Level引入依赖注入,在Controller的Action中,通常我们要调用业务逻辑服务,业务逻辑的服务接口通常是没必要再引入一层另外的抽象。
另外,在这里大致总结一下,在Action中大概有两种方式来调用业务逻辑,一种利用Command模式,即把每个Action 对一个Command,在Command中再调用业务逻辑服务.一种是每个Controller包含一个或多个粗粒度的业务包装服务类,每个Action中直接调用一个或多个服务类处理。具体使用那一种模式,应根据你的项目情况来决定,这里不展开细说了。
最后所有的测试代码可以在这里下载http://files.cnblogs.com/jjyjjyjjy/TestAsp_Net_Mvc_ControllerActivator_DI.rar