• 用工厂模式解决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();
        }
    }
  • 相关阅读:
    守卫者的挑战
    黑魔法师之门
    noip2015 普及组
    noip2015 提高组day1、day2
    40026118素数的个数
    高精度模板
    经典背包系列问题
    修篱笆
    [LintCode] Linked List Cycle
    [LintCode] Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/dudu/p/6866944.html
Copyright © 2020-2023  润新知