zoukankan      html  css  js  c++  java
  • 在.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

  • 相关阅读:
    详解并发和并行意义
    MoleHill Getting Started AGAL(转)
    解决setInterval计时器不准的问题
    Flash视频播放器开发经验总结
    利用pushState开发无刷页面切换
    用小乐图客抓取人人网照片
    如何在Flash中新窗口打开页面而不被拦截
    响应式开发总结
    用Asroute解决复杂状态切换问题
    对iframe跨域通信的封装
  • 原文地址:https://www.cnblogs.com/catcher1994/p/handle-multi-implementations-with-same-interface-in-dotnet-core.html
Copyright © 2011-2022 走看看