赞
踩
本系列主要是对.NET8.0和C#12做一些新特性的操作说明,以及对我们平时开发中有影响的一些技术分享。
今天先说一下第一个新特性,就是KeyedService在一对多的依赖注入中的使用(也就是一个接口有多个实现类继承的情况)。
以下的代码,是通过原生的依赖注入来讲解的,其他的第三方框架,可以自己自定义扩展。
首先,我们就来模拟一下数据:
- /// <summary>
- /// 1、定义一个接口
- /// </summary>
- public interface IMoreImplService
- {
- string SayWelocome();
- }
-
- /// <summary>
- /// 2、分别定义两个实现类
- /// </summary>
- public class WelcomeChineseService : IMoreImplService
- {
- public string SayWelocome()
- {
- return "欢迎";
- }
- }
-
-
- public class WelcomeEnglishService : IMoreImplService
- {
- public string SayWelocome()
- {
- return "Welcome";
- }
- }
然后我们准备好了,该注入了,你可能会说,简单呀!直接注入然后调用不就好了:
- builder.Services.
- AddScoped<IMoreImplService, WelcomeChineseService>();
- builder.Services.
- AddScoped<IMoreImplService, WelcomeEnglishService>();
然后直接调用
这个时候直接预览效果
可以看到是 Welcome ,正好和我们的注入顺序是一致的,你可以把顺序换一下,打印的内容也会发生变化,既然是注入了多个,那就把多个实例都拿出来:
- /// <summary>
- /// 1、将多个接口实例关系全部注入
- /// </summary>
- /// <param name="moreImplServices"></param>
- public WeatherForecastController(IEnumerable<IMoreImplService> moreImplServices)
- {
- // 注意这里是复数
- _moreImplServices = moreImplServices;
- }
-
-
- [HttpGet]
- public object Get()
- {
- var result = "";
- // 调用多次输出
- foreach (var item in _moreImplServices)
- {
- result += item.SayWelocome() + "\n";
- }
-
-
- return result;
- }
把两个实例都打印了出来,这就说明 容器里就有多个接口实例映射关系 ,只是我们在 controller 控制器里取的时候,只获取了最后一个!
所以我们就可以通过别名的形式给每个实例对象来做个标志。
- /// <summary>
- /// 定义一个服务工厂,Singleton注入
- /// 注意这个不是真正意义上的工厂,只是提供服务的存取
- /// </summary>
- public class SingletonFactory
- {
- // 定义一个字典,存储我们的接口服务和别名
- Dictionary<Type, Dictionary<string, object>> serviceDict;
- public SingletonFactory()
- {
- serviceDict = new Dictionary<Type, Dictionary<string, object>>();
- }
-
-
- /// <summary>
- /// 根据别名,获取接口实例
- /// </summary>
- /// <typeparam name="TService"></typeparam>
- /// <param name="id"></param>
- /// <returns></returns>
- public TService GetService<TService>(string id) where TService : class
- {
- // 获取接口的类型
- var serviceType = typeof(TService);
- // out 方法,先获取某一个接口下的,<别名,实例>字典
- if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
- {
- // 根据别名,获取接口的实例对象
- if (implDict.TryGetValue(id, out object service))
- {
- // 强类型转换
- return service as TService;
- }
- }
- return null;
- }
-
-
-
- /// <summary>
- /// 将实例和别名 匹配存储
- /// </summary>
- /// <typeparam name="TService"></typeparam>
- /// <param name="service"></param>
- /// <param name="id"></param>
- public void AddService<TService>(TService service, string id) where TService : class
- {
- var serviceType = typeof(TService);
- // 对象实例判空
- if (service != null)
- {
- // 如果不存在,则填充
- if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict))
- {
- implDict[id] = service;
- }
- else
- {
- implDict = new Dictionary<string, object>();
- implDict[id] = service;
- serviceDict[serviceType] = implDict;
- }
- }
- }
- }
就是把接口下的实现类,都 new 出来实例,然后匹配上别名,那下一步,我们就需要先把这个单例服务给注入进去:
- SingletonFactory singletonFactory = new SingletonFactory();
- singletonFactory.AddService<IMoreImplService>(new WelcomeChineseService(), "Chinese");
- singletonFactory.AddService<IMoreImplService>(new WelcomeEnglishService(), "English");
-
-
- services.AddSingleton(singletonFactory);
最后我们就来调用看看:
- // 各自定义需要的多个字段
- private readonly IMoreImplService moreImplServiceChinese;
- private readonly IMoreImplService moreImplServiceEnglish;
-
-
-
-
- public WeatherForecastController(SingletonFactory singletonFactory)
- {
- this.singletonFactory = singletonFactory;
-
-
- // 根据别名获取服务
- moreImplServiceChinese = singletonFactory.GetService<IMoreImplService>("Chinese");
- moreImplServiceEnglish = singletonFactory.GetService<IMoreImplService>("English");
- }
-
-
- [HttpGet("/welcome")]
- public object GetWelcome()
- {
- return moreImplServiceChinese.SayWelocome();
- }
结果没问题了,最后我们再来回顾一下这种写法的步骤:
1、定义一个单例服务类,将我们的多个对象new出来实例,和别名对应存储起来;
2、将单例类实例化后,注入服务容器;
3、控制器获取单例类,并根据别名获取相对应的服务;
到了这里,就已经完成了,但是这样不是很简洁,可以使用工厂模式,具体的就不再赘述了,大家可以网上搜索下都有,虽然简单工厂的写法比较正规且简单了,但是还是不够优雅,尽管这种一对多的场景不多,但是有时候还是很有必要的,如果都这么写,肯定不行,而且微软官方也想到了这个问题,这不,直接增加了新的特性。
还是用上边的例子来改造下注入方法:
- builder.Services.
- AddKeyedSingleton<IMoreImplService, WelcomeChineseService>("ch");
- builder.Services.
- AddKeyedSingleton<IMoreImplService, WelcomeEnglishService>("en");
而且也同样的有三个生命周期:
AddKeyedSingleton、AddKeyedScoped、AddKeyedTransient
然后直接调用
- [HttpGet]
- public object Get([FromKeyedServices("en")] IMoreImplService moreImplService)
- {
- return moreImplService.SayWelocome();
- //return _moreImplService.SayWelocome();
- }
所以直接三行代码就能搞定,帮我们省了很多的麻烦,
当然如果别名不对,或者为空,就会报错提示服务注册不存在
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。