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>())
相關文章
相關標籤/搜索