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

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

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

可是在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” 实例,每一个实例注册一次。ide

这个问题是由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的依赖项。在单例状况下,这可能比较适合,但它不是很灵活。ui

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

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>())
相关文章
相关标签/搜索