用工廠模式解決ASP.NET Core中依賴注入的一個煩惱


這是最近在實際開發中遇到的一個問題,用 asp.net core 開發一個后端 web api ,根據指定的 key 清除 2 台 memcached 服務器上的緩存。背景是我們在進行 .net core 遷移工作,asp.net 項目與 asp.net core 項目並存,為了避免兩種類型項目的緩存沖突,我們分別用了 2 台不同的 memcached 服務器。

之前使用 1 台 memcached 服務器時,只需要一個客戶端,所以只需創建一個 MemcachedClient 單例並注入到 IMemcachedClient 接口。

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions();
    services.Configure<MemcachedClientOptions>(Configuration.GetSection("memcached"));
    services.Add(ServiceDescriptor.Transient<IMemcachedClientConfiguration, MemcachedClientConfiguration>());
    services.Add(ServiceDescriptor.Singleton<IMemcachedClient, MemcachedClient>());
}

(注:memcached 的配置存儲在 appsettings.json 中)

而現在需要用 2 個 memcached 客戶端實例分別連接 2 台不同的 memcached 服務器,需要 2 個不同配置的 MemcachedClient 單例,而之前針對 1 個 IMemcachedClient 接口的依賴注入方法不管用了。咋整?

首先想到的是一個變通的方法,1 個接口不行,那就用 2 個接口,於是增加下面的 2 個接口:

public interface IMemcachedClientCore : IMemcachedClient
{
}
public interface IMemcachedClientLegacy : IMemcachedClient {
}

因為 MemcachedClient 並沒有實現這個這 2 個接口,還要另外增加這 2 個接口的實現:

public class MemcachedClientCore : MemcachedClient, IMemcachedClientCore
{
    public MemcachedClientCore(
        ILogger<MemcachedClient> logger,
        IMemcachedClientConfiguration configuration)
        : base(logger, configuration)
    {

    }

}

public class MemcachedClientLegacy : MemcachedClient, IMemcachedClientLegacy
{
    public MemcachedClientLegacy(
        ILogger<MemcachedClient> logger,
        IMemcachedClientConfiguration configuration)
        : base(logger, configuration)
    {

    }

}

沿着這條路發現越走越不對勁,還要增加更多的接口與實現。由於 2 個 memcached 客戶端的不同在於 IMemcachedClientConfiguration 的不同,而上面的  MemcachedClientCore 與  MemcachedClientLegacy 的構造函數都注入 IMemcachedClientConfiguration 是不行的,還要基於 IMemcachedClientConfiguration 再增加 2 個接口,增加了接口就又不得不再增加實現。。。這樣解決問題豈不讓人瘋掉,遂棄之。

后來轉念一想,自己解決問題的思路走偏了,一味地將關注的焦點放在如何通過 Dependency Injection 注入 2 個不同的 MemcachedClient 實例,而忽略了一個很簡單的解決方法 —— 用工廠類創建 MemcachedClient 實例,通過 Dependency Injection 注入工廠類,就像 ILoggerFactory 那樣。

於是通過基於依賴注入的工廠模式輕松解決了這個問題。

定義一個 IMemcachedClientFactory 接口:

public interface
{
    IMemcachedClientFactory Add(string keyOfConfiguration);
    IMemcachedClient Create(string keyOfConfiguration);        
}

添加 MemcachedClientFactory 類實現 IMemcachedClientFactory 接口:

public class MemcachedClientFactory : IMemcachedClientFactory
{
    private readonly ILoggerFactory _loggerFactory;
    private readonly IConfiguration _configuration;
    private readonly Dictionary<string, IMemcachedClient> _clients = new Dictionary<string, IMemcachedClient>();

    public MemcachedClientFactory(
        ILoggerFactory loggerFactory,
        IConfiguration configuration)
    {
        _loggerFactory = loggerFactory;
        _configuration = configuration;
    }

    public IMemcachedClientFactory Add(string keyOfConfiguration)
    {
        var options = new MemcachedClientOptions();
        _configuration.GetSection(keyOfConfiguration).Bind(options);

        var memcachedClient = new MemcachedClient(
            _loggerFactory,
            new MemcachedClientConfiguration(_loggerFactory, options));

        _clients.Add(keyOfConfiguration, memcachedClient);

        return this;
    }

    public IMemcachedClient Create(string keyOfConfiguration)
    {
        return _clients[keyOfConfiguration];
    }
}

在 Startup.ConfigureServices() 中注入 MemcachedClientFactory 的單例:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IMemcachedClientFactory, MemcachedClientFactory>();
}

在 Startup.Configure() 中調用 IMemcachedClientFactory 接口的 Add() 方法,根據不同配置創建 MemcachedClient 的實例:

public void Configure(IApplicationBuilder app, IMemcachedClientFactory memcachedClientFactory)
{
    memcachedClientFactory.Add("MemcachedLegacy").Add("MemcachedCore");
}

在使用  MemcachedClient 的地方通過 IMemcachedClientFactory 接口的 Create() 方法獲取所需 MemcachedClient 的實例:

public class CacheController : Controller
{
    private readonly IMemcachedClient _memcachedClientLegacy;
    private readonly IMemcachedClient _memcachedClientCore;

    public CacheController(IMemcachedClientFactory memcachedClientFactory)
    {
        _memcachedClientLegacy = memcachedClientFactory.Create("MemcachedLegacy");
        _memcachedClientCore = memcachedClientFactory.Create("MemcachedCore");
    }

    [HttpDelete("{key}")]
    public async Task<IActionResult> Delete(string key)
    {
        var removeCoreTask =  _memcachedClientCore.RemoveAsync(key);
        var removeLegacyTask = _memcachedClientLegacy.RemoveAsync(key);
        await removeCoreTask;
        await removeLegacyTask;
        return Ok();
    }
}


免責聲明!

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



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