• .NET依赖注入之一个接口多个实现


    前言

    最近又在项目中碰到需要将原本单实现的接口改造成多个实现的场景,这里记录一下常见的几种改法。

    假设已经存在如下接口ICustomService和其实现CustomService,由于只有一种实现,注入和使用非常容易。

    public interface ICustomService
    {
    void MethodA();
    void MethodB();
    }
    public class CustomService: ICustomService
    {
    public void MethodA()
    {
    }
    public void MethodB()
    {
    }
    }
    //注入
    builder.Services.AddTransient();
    //使用
    private readonly ICustomService _customService;
    public CustomController(ICustomService customService)
    {
    _customService = customService;
    }

    现在我们需要增加一种实现。

    使用多个接口实现

    我们可以将原ICustomService内的方法移到到一个新的基接口,共享出来,需要多少个实现,就创建多少个空接口继承该基接口。

    //基接口
    public interface ICustomBaseService
    {
    void MethodA();
    void MethodB();
    }
    //多个空接口
    public interface ICustomService : ICustomBaseService
    {
    }
    public interface ICustomServiceV2 : ICustomBaseService
    {
    }
    //第一种实现
    public class CustomService: ICustomService
    {
    public void MethodA()
    {
    }
    public void MethodB()
    {
    }
    }
    //第二种实现
    public class CustomServiceV2: ICustomServiceV2
    {
    public void MethodA()
    {
    }
    public void MethodB()
    {
    }
    }
    //注入
    builder.Services.AddTransient();
    builder.Services.AddTransient();
    //使用
    private readonly ICustomService _customService;
    private readonly ICustomServiceV2 _customServiceV2;
    public CustomController(ICustomService customService,ICustomServiceV2 customServiceV2)
    {
    _customService = customService;
    _customServiceV2 = customServiceV2;
    }

    这种实现方式需要增加了一套空接口做隔离,看似可能比较“浪费”,但后期随着项目的演进,ICustomServiceICustomServiceV2可能会慢慢分化,我们可以很方便的为它们扩充各种独有方法。

    使用单接口实现

    如果我们确定不需要多个接口,也可以使用下面的单接口实现

    public interface ICustomService
    {
    void MethodA();
    void MethodB();
    }
    //第一种实现
    public class CustomService: ICustomService
    {
    public void MethodA()
    {
    }
    public void MethodB()
    {
    }
    }
    //第二种实现
    public class CustomServiceV2: ICustomService
    {
    public void MethodA()
    {
    }
    public void MethodB()
    {
    }
    }
    //注入
    builder.Services.AddTransient();
    builder.Services.AddTransient();
    //使用
    private readonly ICustomService _customService;
    private readonly ICustomService _customServiceV2;
    public CustomController(IEnumerable customServices)
    {
    _customService = customServices.ElementAt(0);
    _customServiceV2 = customServices.ElementAt(1);
    }

    从上面代码可以看到,我们是为从接口ICustomService注册两个实现,并从IEnumerable解析出了这两个实现。这里可能会有两个疑问

    1. 为什么第一个实现CustomService没有被第二个实现CustomServiceV2替换掉?
    2. 为什么可以从IEnumerable解析到我们需要的服务?

    答案在Microsoft.Extensions.DependencyInjection.ServiceDescriptorMicrosoft.Extensions.DependencyInjection.ServiceCollection 这两个类里,进程里,依赖注入的服务,会被添加到ServiceCollection里,ServiceCollection是一组ServiceDescriptor的集合,ServiceDescriptor通过服务类型、实现以及生命周期三个组合在一起构成的标识来确定服务。而ICustomService+CustomService+TransientICustomService+CustomServiceV2+Transient是两个不同的ServiceDescriptor,因此不会被替换。同时服务类型的ServiceDescriptor会被聚合在一起,于是我们可以很方便的从IEnumerable对象中解析出所有的同类型的服务。

    总结

    本质上,两种方法都是多态性(Polymorphism)的应用,没有优劣之分,根据场景选择合适的写法。

    链接

    https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
    https://github.com/dotnet/runtime

  • 相关阅读:
    【无人机】基于自适应无人机的湍流下发动机故障不确定性自动着陆问题(Matlab代码实现)
    多模态大模型训练数据集汇总介绍
    PostgreSQL的学习心得和知识总结(九十四)|深入理解PostgreSQL数据库开源MPP扩展Citus DDL命令下发 的实现原理
    Linux内核——IEEE 802.15.4开发者指南
    [附源码]java毕业设计疫情环境下的酒店管理系统
    Nacos配置服务的源码解析(全)
    Win10系统磁盘问题----- 分区、c盘空间清理、扩展卷、恢复分区解决办法合集
    安卓性能优化
    9.复杂的例子:模块的使用和自定义模块
    web课程设计使用html+css+javascript+jquery技术制作个人介绍6页
  • 原文地址:https://www.cnblogs.com/netry/p/net-dependency-injection-multiple-implementations.html