当前位置:   article > 正文

【.NET8.0 新特性系列】依赖注入一对多模式变的超简单

addkeyedsingleton

本系列主要是对.NET8.0和C#12做一些新特性的操作说明,以及对我们平时开发中有影响的一些技术分享。

今天先说一下第一个新特性,就是KeyedService在一对多的依赖注入中的使用(也就是一个接口有多个实现类继承的情况)。

3864a18f5c4d5eb887c885f8473df896.png

以下的代码,是通过原生的依赖注入来讲解的,其他的第三方框架,可以自己自定义扩展。

 一、8.0之前我们是如何做

首先,我们就来模拟一下数据:

  1. /// <summary>
  2.  /// 1、定义一个接口
  3. /// </summary>
  4. public interface IMoreImplService
  5. {
  6. string SayWelocome();
  7.  }
  8.  
  9.  /// <summary>
  10. /// 2、分别定义两个实现类
  11. /// </summary>
  12. public class WelcomeChineseService : IMoreImplService
  13. {
  14. public string SayWelocome()
  15. {
  16. return "欢迎";
  17. }
  18. }
  19. public class WelcomeEnglishService : IMoreImplService
  20. {
  21. public string SayWelocome()
  22. {
  23. return "Welcome";
  24. }
  25.  }

然后我们准备好了,该注入了,你可能会说,简单呀!直接注入然后调用不就好了:

  1. builder.Services.
  2. AddScoped<IMoreImplService, WelcomeChineseService>();
  3.  builder.Services.
  4.   AddScoped<IMoreImplService, WelcomeEnglishService>();

然后直接调用

ef2d303fc79940090cf8d3f1934d3420.png

这个时候直接预览效果

6feff010697f72b3c99be8eb4add664b.png

可以看到是 Welcome ,正好和我们的注入顺序是一致的,你可以把顺序换一下,打印的内容也会发生变化,既然是注入了多个,那就把多个实例都拿出来:

  1. /// <summary>
  2.  /// 1、将多个接口实例关系全部注入
  3. /// </summary>
  4. /// <param name="moreImplServices"></param>
  5. public WeatherForecastController(IEnumerable<IMoreImplService> moreImplServices)
  6. {
  7. // 注意这里是复数
  8. _moreImplServices = moreImplServices;
  9. }
  10. [HttpGet]
  11. public object Get()
  12. {
  13. var result = "";
  14. // 调用多次输出
  15. foreach (var item in _moreImplServices)
  16. {
  17. result += item.SayWelocome() + "\n";
  18. }
  19. return result;
  20.  }

1277ca5d11cd6d23ba59a4c763a8dc69.png

把两个实例都打印了出来,这就说明 容器里就有多个接口实例映射关系 ,只是我们在 controller 控制器里取的时候,只获取了最后一个!

所以我们就可以通过别名的形式给每个实例对象来做个标志。

  1. /// <summary>
  2.  /// 定义一个服务工厂,Singleton注入
  3. /// 注意这个不是真正意义上的工厂,只是提供服务的存取
  4. /// </summary>
  5. public class SingletonFactory
  6. {
  7. // 定义一个字典,存储我们的接口服务和别名
  8. Dictionary<Type, Dictionary<string, object>> serviceDict;
  9. public SingletonFactory()
  10. {
  11. serviceDict = new Dictionary<Type, Dictionary<string, object>>();
  12. }
  13. /// <summary>
  14. /// 根据别名,获取接口实例
  15. /// </summary>
  16. /// <typeparam name="TService"></typeparam>
  17. /// <param name="id"></param>
  18. /// <returns></returns>
  19. public TService GetService<TService>(string id) where TService : class
  20. {
  21. // 获取接口的类型
  22. var serviceType = typeof(TService);
  23. // out 方法,先获取某一个接口下的,<别名,实例>字典
  24. if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
  25. {
  26. // 根据别名,获取接口的实例对象
  27. if (implDict.TryGetValue(id, out object service))
  28. {
  29. // 强类型转换
  30. return service as TService;
  31. }
  32. }
  33. return null;
  34. }
  35. /// <summary>
  36. /// 将实例和别名 匹配存储
  37. /// </summary>
  38. /// <typeparam name="TService"></typeparam>
  39. /// <param name="service"></param>
  40. /// <param name="id"></param>
  41. public void AddService<TService>(TService service, string id) where TService : class
  42. {
  43. var serviceType = typeof(TService);
  44. // 对象实例判空
  45. if (service != null)
  46. {
  47. // 如果不存在,则填充
  48. if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
  49. {
  50. implDict[id] = service;
  51. }
  52. else
  53. {
  54. implDict = new Dictionary<string, object>();
  55. implDict[id] = service;
  56. serviceDict[serviceType] = implDict;
  57. }
  58. }
  59. }
  60.  }

就是把接口下的实现类,都 new 出来实例,然后匹配上别名,那下一步,我们就需要先把这个单例服务给注入进去:

  1. SingletonFactory singletonFactory = new SingletonFactory();
  2. singletonFactory.AddService<IMoreImplService>(new WelcomeChineseService(), "Chinese");
  3. singletonFactory.AddService<IMoreImplService>(new WelcomeEnglishService(), "English");
  4.  services.AddSingleton(singletonFactory);

最后我们就来调用看看:

  1. // 各自定义需要的多个字段
  2. private readonly IMoreImplService moreImplServiceChinese;
  3. private readonly IMoreImplService moreImplServiceEnglish;
  4. public WeatherForecastController(SingletonFactory singletonFactory)
  5. {
  6. this.singletonFactory = singletonFactory;
  7. // 根据别名获取服务
  8. moreImplServiceChinese = singletonFactory.GetService<IMoreImplService>("Chinese");
  9. moreImplServiceEnglish = singletonFactory.GetService<IMoreImplService>("English");
  10. }
  11. [HttpGet("/welcome")]
  12. public object GetWelcome()
  13. {
  14. return moreImplServiceChinese.SayWelocome();
  15. }

结果没问题了,最后我们再来回顾一下这种写法的步骤:

1、定义一个单例服务类,将我们的多个对象new出来实例,和别名对应存储起来;

2、将单例类实例化后,注入服务容器;

3、控制器获取单例类,并根据别名获取相对应的服务;

到了这里,就已经完成了,但是这样不是很简洁,可以使用工厂模式,具体的就不再赘述了,大家可以网上搜索下都有,虽然简单工厂的写法比较正规且简单了,但是还是不够优雅,尽管这种一对多的场景不多,但是有时候还是很有必要的,如果都这么写,肯定不行,而且微软官方也想到了这个问题,这不,直接增加了新的特性。

 二、8.0新特性KeyedService

还是用上边的例子来改造下注入方法:

  1. builder.Services.
  2. AddKeyedSingleton<IMoreImplService, WelcomeChineseService>("ch");
  3. builder.Services.
  4. AddKeyedSingleton<IMoreImplService, WelcomeEnglishService>("en");

而且也同样的有三个生命周期:

AddKeyedSingleton、AddKeyedScoped、AddKeyedTransient

然后直接调用

  1. [HttpGet]
  2. public object Get([FromKeyedServices("en")] IMoreImplService moreImplService)
  3. {
  4. return moreImplService.SayWelocome();
  5. //return _moreImplService.SayWelocome();
  6. }

所以直接三行代码就能搞定,帮我们省了很多的麻烦,

当然如果别名不对,或者为空,就会报错提示服务注册不存在

2dc3d49e5eb4577fd2c15dc7a6b54b43.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/136438
推荐阅读
相关标签
  

闽ICP备14008679号