在.NET Core中處理一個接口多個不同實現的依賴注入問題


前言

近段時間在准備公司的技術分享,所以這段時間將大部分時間放在准備分享內容上去了。博客也就停了一下下。

在.NET Core中處理依賴注入問題時,往往是定義好了一個操作規范的接口,會有N多個基於不同技術的實現,根據實際情況在項目中去使用某一個實現。

但是偶爾會出現這樣的情況,在某一個地方,需要同時使用到兩種或兩種以上的實現,這個時候我們要怎么處理呢?

借助Autofac等第三方組件時,是可以很容易的實現,但是在寫一些基礎類庫時會避免直接引用太多依賴組件。

所以這里是只用微軟自帶的DI(Microsoft.Extensions.DependencyInjection)去處理。

例子引入

現在有一個接口和兩個實現類。

public interface IDemoService
{
    string Get();
}

public class DemoServiceA : IDemoService
{
    public string Get()
    {
        return "Service A";
    }
}

public class DemoServiceB : IDemoService
{
    public string Get()
    {
        return "Service B";
    }
}

常規的方法,我們先在Startup中的ConfigureServices方法中添加我們的service。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDemoService, DemoServiceA>();
    services.AddSingleton<IDemoService, DemoServiceB>();

    services.AddMvc();
}

然后在控制器中使用

private IDemoService _serviceA;

private IDemoService _serviceB;

public ValuesController(IDemoService serviceA, IDemoService serviceB)
{
    _serviceA = serviceA;
    _serviceB = serviceB;
}

// GET api/values
[HttpGet]
public string Get()
{
    return $"{_serviceA.Get()}-{_serviceB.Get()}";
}

我們的預期結果是:Service A-Service B,可是上面代碼的實際結果卻並不像我們想的那么簡單!!

可以看到這里輸出的都是Service B,連Service A的影子都沒有看到。

其實,從代碼都可以看出來,它只能拿到其中一個Service的實現類!

那么我們要息怎樣處理才能達到我們想要的效果呢?

其實思路比較簡單,上面導致不能拿到對應實現類,本質上來講應該說是它區分不了那個才是想要的!我們想個辦法讓它能區分就好了。

處理方法

給我們的Service起個別名!

先是Startup中的ConfigureServices方法。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<DemoServiceA>();
    services.AddSingleton<DemoServiceB>();

    services.AddSingleton(factory =>
    {
        Func<string, IDemoService> accesor = key =>
        {
            if (key.Equals("ServiceA"))
            {
                return factory.GetService<DemoServiceA>();
            }
            else if (key.Equals("ServiceB"))
            {
                return factory.GetService<DemoServiceB>();
            }
            else
            {
                throw new ArgumentException($"Not Support key : {key}");
            }
        };
        return accesor;
    });

    services.AddMvc();
}

這里並沒有直接向上面那樣一次性指定接口和對應的實現類,而是用了AddSingleton的另一個重載方法。

  1. 先將實現類注冊一下
  2. 然后再注冊一下Func<string, IDemoService>

先來說說這個Func<string, IDemoService>里面的string和IDemoService都分別代表什么。

  • string 毫無疑問就是我們上面說到的別名
  • IDemoService 這個就是我們要用的Service

核心在於,factory參數是IServiceProvider類型的!所以我們可以根據這個factory去找到我們前面注冊的實現類。這樣解釋一下,是不是就清晰了呢?

然后再來看看在控制器上面怎么用。

private IDemoService _serviceA;

private IDemoService _serviceB;

private readonly Func<string, IDemoService> _serviceAccessor;

public ValuesController(Func<string, IDemoService> serviceAccessor)
{
    this._serviceAccessor = serviceAccessor;

    _serviceA = _serviceAccessor("ServiceA");
    _serviceB = _serviceAccessor("ServiceB");
}

// GET api/values
[HttpGet]
public string Get()
{
    return $"{_serviceA.Get()}-{_serviceB.Get()}";
}

最后看看結果是不是和我們的預期一樣。

結果與預期一致。

總結

一對一,或許是最好的方法,也是最為理想的,這樣能避開很多不必要的問題。但是現實中總會出現特殊情況,面對這些特殊情況,我們也是需要能夠重容的面對。

如果您有更好的處理方法,也可以留言討論。

文中的示例代碼 DIDemo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM