標籤:private client asp tor text 項目 top icon 建構函式
這是最近在實際開發中遇到的一個問題,用 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 _loggerFacotry; private readonly IConfiguration _configuration; private readonly Dictionary<string, IMemcachedClient> _clients = new Dictionary<string, IMemcachedClient>(); public MemcachedClientFactory( ILoggerFactory loggerFacotry, IConfiguration configuration) { _loggerFacotry = loggerFacotry; _configuration = configuration; } public IMemcachedClientFactory Add(string keyOfConfiguration) { var options = new MemcachedClientOptions(); _configuration.GetSection(keyOfConfiguration).Bind(options); var memcachedClient = new MemcachedClient( _loggerFacotry, new MemcachedClientConfiguration(_loggerFacotry, 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 memcachedClientFacotry) { _memcachedClientLegacy = memcachedClientFacotry.Create("MemcachedLegacy"); _memcachedClientCore = memcachedClientFacotry.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(); }}
用原廠模式解決ASP.NET Core中依賴注入的一個煩惱