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

打印 上一主题 下一主题

主题 510|帖子 510|积分 1530

前言

最近又在项目中碰到需要将原本单实现的接口改造成多个实现的场景,这里记录一下常见的几种改法。
假设已经存在如下接口ICustomService和其实现CustomService,由于只有一种实现,注入和使用非常容易。
  1. public interface ICustomService
  2. {
  3.     void MethodA();
  4.     void MethodB();
  5. }
  6. public class CustomService: ICustomService
  7. {
  8.     public void MethodA()
  9.     {
  10.     }
  11.     public void MethodB()
  12.     {
  13.     }
  14. }
  15. //注入
  16. builder.Services.AddTransient<ICustomService, CustomService>();
  17. //使用
  18. private readonly ICustomService _customService;
  19. public CustomController(ICustomService customService)
  20. {
  21.   _customService = customService;
  22. }
复制代码
现在我们需要增加一种实现。
使用多个接口实现

我们可以将原ICustomService内的方法移到到一个新的基接口,共享出来,需要多少个实现,就创建多少个空接口继承该基接口。
  1. //基接口
  2. public interface ICustomBaseService
  3. {
  4.     void MethodA();
  5.     void MethodB();
  6. }
  7. //多个空接口
  8. public interface ICustomService : ICustomBaseService
  9. {
  10. }
  11. public interface ICustomServiceV2 : ICustomBaseService
  12. {
  13. }
  14. //第一种实现
  15. public class CustomService: ICustomService
  16. {
  17.     public void MethodA()
  18.     {
  19.     }
  20.     public void MethodB()
  21.     {
  22.     }
  23. }
  24. //第二种实现
  25. public class CustomServiceV2: ICustomServiceV2
  26. {
  27.     public void MethodA()
  28.     {
  29.     }
  30.     public void MethodB()
  31.     {
  32.     }
  33. }
  34. //注入
  35. builder.Services.AddTransient<ICustomService, CustomService>();
  36. builder.Services.AddTransient<ICustomServiceV2, CustomServiceV2>();
  37. //使用
  38. private readonly ICustomService _customService;
  39. private readonly ICustomServiceV2 _customServiceV2;
  40. public CustomController(ICustomService customService,ICustomServiceV2 customServiceV2)
  41. {
  42.   _customService = customService;
  43.   _customServiceV2 = customServiceV2;
  44. }
复制代码
这种实现方式需要增加了一套空接口做隔离,看似可能比较“浪费”,但后期随着项目的演进,ICustomService和ICustomServiceV2可能会慢慢分化,我们可以很方便的为它们扩充各种独有方法。
使用单接口实现

如果我们确定不要要多个接口,也可以使用下面的单接口实现
  1. public interface ICustomService
  2. {
  3.     void MethodA();
  4.     void MethodB();
  5. }
  6. //第一种实现
  7. public class CustomService: ICustomService
  8. {
  9.     public void MethodA()
  10.     {
  11.     }
  12.     public void MethodB()
  13.     {
  14.     }
  15. }
  16. //第二种实现
  17. public class CustomServiceV2: ICustomService
  18. {
  19.     public void MethodA()
  20.     {
  21.     }
  22.     public void MethodB()
  23.     {
  24.     }
  25. }
  26. //注入
  27. builder.Services.AddTransient<ICustomService, CustomService>();
  28. builder.Services.AddTransient<ICustomService, CustomServiceV2>();
  29. //使用
  30. private readonly ICustomService _customService;
  31. private readonly ICustomServiceV2 _customServiceV2;
  32. public CustomController(IEnumerable<ICustomService> customServices)
  33. {
  34. _customService = customServices.ElementAt(0);
  35. _customServiceV2 = customServices.ElementAt(1);
  36. }
复制代码
从上面代码可以看到,我们是为从接口ICustomService注册两个实现,并从IEnumerable解析出了这两个实现。这里可能会有两个疑问

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

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

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

祗疼妳一个

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表