ASP.NETMVC3:正确实现UnityDependencyResolver
前日,dudu 写了篇文章 《想爱容易,相处难:当ASP.NET MVC 爱上 IoC》,介绍了在 MVC 中如何使用 Unity,不过 dudu 犯了一个错误:错误地使用了 Unity。
这要先从 Unity 使用说起:
Unity 基本使用
假定程序中有如下两个接口:
1 2 |
public interface ICustomerRepository { /*...*/ } public interface IOrderRepository { /*...*/ } |
和两个实现类:
1 2 |
public class CustomerRepository : ICustomerRepository { /*...*/ } public class OrderRepository : IOrderRepository { /*...*/ } |
可以如下使用:
1 2 3 4 5 6 7 |
var container = new UnityContainer(); //注册 container.RegisterType<ICustomerRepository, CustomerRepository>(); container.RegisterType<IOrderRepository, OrderRepository>(); //使用 var customerRepository = container.Resolve<ICustomerRepository>(); var orderRepository = container.Resolve<IOrderRepository>(); |
(可在配置文件中注册,请参考相关文档)
但是实际使用中,情况要复杂的多,如下面这个接口和类(如何注入?):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public interface IOrderService { /*...*/ } public class OrderService: IOrderService { private ICustomerRepository customerRepository; private IOrderRepository orderRepository; public OrderService(ICustomerRepository customerRepository, IOrderRepository orderRepository) { this.customerRepository = customerRepository; this.orderRepository = orderRepository; } /*...*/ } |
有朋友会说,可以使用构造函数注入:
1 2 3 4 |
container.RegisterType<IOrderService, OrderService>( new InjectionConstructor(new CustomerRepository(), new OrderRepository()) ); var orderService = container.Resolve<IOrderService>(); |
很好,这个这样可以解决这个问题。
但是,这种方式是存在一些缺陷的:
1、多次注册:我们已经将 CustomerRepository 注册给了 ICustomerRepository 接口,但在注册 IOrderService 接口时,还要 new CustomerRepository。多次注册会带来很多问题,假想有一天,我们需要将 CustomerRepository 统统换成 ImprovedCustomerRepository,使用这种方式不得不多处修改。
2、设计变动时,多处修改:设想 OrderService 的构造函数需要增加一个 IProductRepository 类型的参数,另外一个类 StoreService 的构造函数也要增加这么个参数。
3、更复杂的情况,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public interface IOrderController { /*...*/ } public class OrderController: IOrderController { private IOrderService orderService; private IStoreService storeService; public OrderController(IOrderService orderService, IStoreService storeService) { this.orderService = orderService; this.storeService = storeService; } } |
如何在 Unity 中注册 IOrderController? 别告诉我用构造函数注入,这可得两级构造注入。
其实 Unity 支持 Circular References,可以轻松解决这些问题:
使用 Circular References
Circular References 翻译过来是“循环引用“。
Unity 中,不需要任何设置就可以使用 Circular References,使用一个例子来说明 :
1 2 3 4 5 6 7 |
var container = new UnityContainer(); container.RegisterType<ICustomerRepository, CustomerRepository>(); container.RegisterType<IOrderRepository, OrderRepository>(); container.RegisterType<IOrderService, OrderService>(); container.RegisterType<IOrderController, OrderController>(); var orderController = container.Resolve<IOrderController>(); |
说明(相当啰嗦,明白人可跳过):Unity 在获取 IOrderController 的实例时(第 7 行),根据第 5 行的注册得知,应该创建 OrderController 的实例。但 OrderController 有两个参数,类型分别是 IOrderService、IStoreService,势必先创建 IOrderService 的实例。根据第 4 行得知应该去创建 OrderService 的实例,OrderServer 又有 ICustomerRepository、IOrderRepository 两个参数,再根据第 2、3 行分别创建 CustomerRepository 和 OrderRepository 的实例,因为这两个类构造函数无参,直接可实例化。获取 IOrderService 实例化后,用类似的方式再获取 IStoreService 的实例。这样根据构造函数的参数,一步步向前,称为 Circular References Resolve。
有了 Circular References,不再需要过多的注册,Unity 会智能判断并处理。所以,即使一个类型没有注册在容器中,依然可以获取它的实例:
1 2 3 4 5 6 7 |
var container = new UnityContainer(); container.RegisterType<ICustomerRepository, CustomerRepository>(); container.RegisterType<IOrderRepository, OrderRepository>(); container.RegisterType<IOrderService, OrderService>(); bool isRegistered = container.IsRegistered<OrderController>(); //false var controller = container.Resolve<OrderController>(); |
源码下载:UnityDemo.rar (522KB)
dudu 文中存在的问题
看了前面的部分,相信你一定能指出 dudu 文中 UnityDependencyResolver 存在问题,原代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class UnityDependencyResolver : IDependencyResolver { IUnityContainer container; public UnityDependencyResolver(IUnityContainer container) { this.container = container; } public object GetService(Type serviceType) { if (!this.container.IsRegistered(serviceType)) { return null; } return container.Resolve(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return container.ResolveAll(serviceType); } } |
没错,问题就在第 12~15 行,if (!this.container.IsRegistered(serviceType)) return null; 这句其实 ”屏蔽“ 了 Unity 的 Circular References,导致一系列问题,应该去除这四行代码。
另外,使用 Unity (或其它 DI 框架),不需要创建新的 ControllerFactory(如 dudu 文中的 UnityControllerFactory),除非有特殊需要。
从 ControllerBuilder 的源码中,可以看出,如果没有注册 IControllerFactory,MVC 将自动使用 DefaultControllerFactory:
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 |
public class ControllerBuilder { private Func<IControllerFactory> _factoryThunk = () => null; private static ControllerBuilder _instance = new ControllerBuilder(); private IResolver<IControllerFactory> _serviceResolver; public ControllerBuilder() : this(null) { } internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) { _serviceResolver = serviceResolver new SingleServiceResolver<IControllerFactory>( () => _factoryThunk(), new DefaultControllerFactory { ControllerBuilder = this }, "ControllerBuilder.GetControllerFactory" ); } public static ControllerBuilder Current { get { return _instance; } } public IControllerFactory GetControllerFactory() { return _serviceResolver.Current; } public void SetControllerFactory(IControllerFactory controllerFactory) { if (controllerFactory == null) throw new ArgumentNullException("controllerFactory"); _factoryThunk = () => controllerFactory; } //... } |
DefaultControllerFactory 在内部(DefaultControllerActivator 类)会尝试调用 DependencyResolver 来获取 Controller 的实例,如果不成功则使用 Activator.CreateInstance :
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 33 34 35 36 37 38 39 |
public class DefaultControllerFactory : IControllerFactory { private IResolver<IControllerActivator> _activatorResolver; private IControllerActivator _controllerActivator; public DefaultControllerFactory() : this(null, null, null) { } public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) { } internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver) { if (controllerActivator != null) _controllerActivator = controllerActivator; else _activatorResolver = activatorResolver new SingleServiceResolver&l |