翼度科技»论坛 编程开发 .net 查看内容

Denpendcy Injection 8.0新功能——KeyedService

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
Denpendcy Injection 8.0新功能——KeyedService

本文只介绍 .NET Denpendcy Injection 8.0新功能——KeyedService,假定读者已熟练使用之前版本的功能。
注册带Key的类

8.0之前,注册一个类往往是AddSingleton(),8.0添加了一个新功能:“可以注册一个带Key的类”AddKeyedSingleton("keyA")。获取服务方法由GetService()变成了GetKeyedService("keyA"),并且调用这两个方法创建出来的对象是不同的。

如果想通过构造函数注入,只需要在参数前面加上特性[FromKeyedServices("keyA")] 即可,特性里的参数就是key的名字。如果想在构造函数中获取key的值则使用特性[ServiceKey]。我们还可以注册时把key设置为KeyedService.AnyKey(这是框架提供的类),只需使用任意非null值作为key就可以获取对象。暂时不支持使用通配符匹配,也许以后会加......
  1. class Bar : IBar
  2. {
  3.     public Bar([ServiceKey] int key, [FromKeyedServices("keyA")] IFoo foo, IServiceProvider root)
  4.     {
  5.         //注意:key的类型要和调用时一致。
  6.         Console.WriteLine($"key:{key},Compare:{foo == root.GetKeyedService<IFoo>("keyA")}");
  7.     }
  8. }
  9. public static class KeyedService
  10. {
  11.     /// Represents a key that matches any key.
  12.     public static object AnyKey { get; } = new AnyKeyObj();
  13.     private sealed class AnyKeyObj
  14.     {
  15.         public override string? ToString() => "*";
  16.     }
  17. }
复制代码
深入理解

8.0之前,获取一个对象需要用到的一个“标识”,比如调用GetService(),这个“标识”就是IFoo;也就是ServiceDescriptor里面的ServiceType。而在8.0后“标识”变成了IFoo+"keyA",也就是ServiceDescriptor里面的ServiceType+新增的ServiceKey。
  1. public class ServiceDescriptor
  2. {
  3.     public object? ServiceKey { get; }
  4.     public Type? ImplementationType => _implementationType;        
  5.     public Type ServiceType { get; }
  6.     public ServiceLifetime Lifetime { get; }
复制代码
对于以前注册的类,ServiceKey默认是null,所以“标识”就是ServiceKey+null。调用GetService()就等于调用GetKeyedService(null)。

再举一个例子:
  1. //类型的注册信息放在_descriptorLookup,8.0前,是通过ServiceType作为字典的键,
  2. //8.0是把ServiceIdentifier(也就是ServiceKey+ServiceType)作为字典的键
  3. //7.0
  4. private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new();
  5. //8.0
  6. private readonly Dictionary<ServiceIdentifier, ServiceDescriptorCacheItem> _descriptorLookup = new();
  7. internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
  8. {
  9.     public object? ServiceKey { get; }
  10.     public Type ServiceType { get; }
  11. }
复制代码

循环引用

前面讲到可以通过[ServiceKey]获取调用时的Key;而没有注册key的服务是无法在构造函数中注入key的值。通过这个功能可以解决循环引用的问题,先看代码。
  1. class Foo : IFoo
  2. {
  3.     //这个构造函数给GetService<IFoo>()使用
  4.     public Foo()
  5.     {
  6.         this.Num = 10;
  7.     }
  8.     //这个构造函数给GetKeyedServices<IFoo>("keyA")使用
  9.     public Foo([ServiceKey] string key, IFoo foo)
  10.     {
  11.         Console.WriteLine($"key:{key},this.Num:{this.Num},foo.Num:{foo.Num}");
  12.     }
  13.     public int Num { get; set; }
  14. }
复制代码

代码执行流程:

  • 1.DI首先获取Foo的所有构造函数并且按构造函数的参数从多到少进行排序
  • 2.遍历所有构造函数,首先获取参数最多的构造函数 Foo([ServiceKey] string key, IFoo foo),开始判断构造函数的参数能否被DI创建
  • 3.DI首先判断string key这个参数,能够创建;然后继续判断第二个参数IFoo foo能否被创建
  • 4.重复第一步
  • 5.重复第二步
  • 6.DI首先判断string key这个参数,不能够创建;所以无法调用构造函数 Foo([ServiceKey] string key, IFoo foo)创建Foo实例
  • 7.继续遍历构造函数,第二个构造函数是无参的,DI能够创建foo对象。
  • 8.对于GetKeyedServices("keyA"),是使用的这个构造函数Foo([ServiceKey] string key, IFoo foo)创建的对象。IFoo foo是使用无参构造函数创建的。
注意点:

  • 参数[ServiceKey] string key一定要写在参数IFoo foo前面,否则就会循环引用
  • 注册服务时,要注册两种(带key的和不带key的都要注册)AddScoped()   .AddKeyedScoped("keyA")
总结

以前的用法往往是接口对应实现类,通过DI获取对象,只需要知道接口的名字,就可以通过GetService方法或者构造函数注入获取对象。
现在是接口+key对应实现类,通过DI获取对象,需要知道接口+key。如果key为null就和以前的用法一模一样。
结束。第一次写文章如有错误,欢迎各位批评指点,谢谢!

来源:https://www.cnblogs.com/tenleft/archive/2023/09/21/17719609.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具