西红柿炒荷花

ASP.NET Core DI中注册具有多个接口的服务

ASP.NET Core的一个关键特性是它使用依赖注入(DI)。该框架围绕“符合容器”抽象设计,允许框架本身使用简单容器,同时还允许插入功能更丰富的第三方容器。

针对多个服务注册实现的一般模式是常见的。大多数第三方DI容器都内置了这个概念。例如:Autofac;

但是在Core 的DI容器中它是不支持的,比如:

public interface IBar {}
public interface IFoo {}

public class Foo : IFoo, IBar {}


[Fact]
public void WhenRegisteredAsSeparateSingleton_InstancesAreNotTheSame()
{
    var services = new ServiceCollection();

    services.AddSingleton<IFoo, Foo>();
    services.AddSingleton<IBar, Foo>();

    var provider = services.BuildServiceProvider();

    var foo1 = provider.GetService<IFoo>(); // An instance of Foo
    var foo2 = provider.GetService<IBar>(); // An instance of Foo

    Assert.Same(foo1, foo2); // FAILS
}

注册的Foo是两个单例IFooIBar,但结果可能不是你所期望的。我们实际上有两个Foo “Singleton” 实例,每个实例注册一次。

这个问题是由David Fowler在2年前提出的,但它已经解决了,有两种不太优雅的解决方案。

1.提供服务实例(仅限Singleton)

[Fact]
public void WhenRegisteredAsInstance_InstancesAreTheSame()
{
    var foo = new Foo(); // The singleton instance
    var services = new ServiceCollection();

    services.AddSingleton<IFoo>(foo);
    services.AddSingleton<IBar>(foo);

    var provider = services.BuildServiceProvider();

    var foo1 = provider.GetService<IFoo>();
    var foo2 = provider.GetService<IBar>();

    Assert.Same(foo1, foo); // PASSES;
    Assert.Same(foo2, foo); // PASSES;
}

这里要注意: 必须在配置时对Foo进行实例化,并且必须知道并提供Foo的依赖项。在单例情况下,这可能比较适合,但它不是很灵活。

此外,如果你想Foo成为每个请求范围的单个实例(Scoped),那么这种方法就不行了,需要第二种方法

2.使用工厂方法实现转发

[Fact]
public void WhenRegisteredAsForwardedSingleton_InstancesAreTheSame()
{
    var services = new ServiceCollection();

    services.AddSingleton<Foo>(); // We must explicitly register Foo
    services.AddSingleton<IFoo>(x => x.GetRequiredService<Foo>()); // Forward requests to Foo
    services.AddSingleton<IBar>(x => x.GetRequiredService<Foo>()); // Forward requests to Foo

    var provider = services.BuildServiceProvider();

    var foo1 = provider.GetService<Foo>(); // An instance of Foo
    var foo2 = provider.GetService<IFoo>(); // An instance of Foo
    var foo3 = provider.GetService<IBar>(); // An instance of Foo

    Assert.Same(foo1, foo2); // PASSES
    Assert.Same(foo1, foo3); // PASSES
}

为了将接口请求“转发”到具体类型,必须做两件事:

  • 使用明确注册具体类型 services.AddSingleton<Foo>()
  • 通过提供工厂函数将接口请求委托给具体类型: services.AddSingleton<IFoo>(x => x.GetRequiredService<Foo>())

 

posted on 2019-06-27 16:22  西红柿炒荷花  阅读(657)  评论(0编辑  收藏  举报

导航