一. 为什么要进行扩展
在前面的分析中,我们知道默认的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