.net mvc 和 webapi中的IOC(Unity实现)

这篇博客讨论了在 ASP.NET MVC 和 WebAPI 中使用 Unity 作为 IOC 容器的问题。作者指出了 dudu 文章中关于 UnityDependencyResolver 的错误,并提供了正确的实现方式。博客涵盖了 Unity 的 Circular References 功能,如何解决复杂的依赖注入问题,以及如何创建基于 Unity 的 ControllerFactory。同时,文章还提到了在 ASP.NET Web API 中应用 IoC 的两种方法:HttpControllerActivator 和 DependencyResolver,展示了如何使用 Unity 和 Ninject 实现这些功能。
摘要由CSDN通过智能技术生成

ASP.NETMVC3:正确实现UnityDependencyResolver

前日,dudu写了篇文章《想爱容易,相处难:当ASP.NETMVC爱上IoC》,介绍了在MVC中如何使用Unity,不过dudu犯了一个错误:错误地使用了Unity。这要先从Unity使用说起:Unity基本使用假定程序中有
 

前日,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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值