首页 > 其他分享 >Denpendcy Injection 8.0新功能——KeyedService

Denpendcy Injection 8.0新功能——KeyedService

时间:2023-09-21 12:01:36浏览次数:39  
标签:8.0 Denpendcy ServiceKey IFoo key Injection foo public 构造函数

Denpendcy Injection 8.0新功能——KeyedService

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

注册带Key的类

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

如果想通过构造函数注入,只需要在参数前面加上特性[FromKeyedServices("keyA")] 即可,特性里的参数就是key的名字。如果想在构造函数中获取key的值则使用特性[ServiceKey]。我们还可以注册时把key设置为KeyedService.AnyKey(这是框架提供的类),只需使用任意非null值作为key就可以获取对象。暂时不支持使用通配符匹配,也许以后会加......

class Bar : IBar
{
    public Bar([ServiceKey] int key, [FromKeyedServices("keyA")] IFoo foo, IServiceProvider root)
    {
        //注意:key的类型要和调用时一致。
        Console.WriteLine($"key:{key},Compare:{foo == root.GetKeyedService<IFoo>("keyA")}");
    }
}
public static class KeyedService
{
    /// Represents a key that matches any key.
    public static object AnyKey { get; } = new AnyKeyObj();
    private sealed class AnyKeyObj
    {
        public override string? ToString() => "*";
    }
}

深入理解

8.0之前,获取一个对象需要用到的一个“标识”,比如调用GetService<IFoo>(),这个“标识”就是IFoo;也就是ServiceDescriptor里面的ServiceType。而在8.0后“标识”变成了IFoo+"keyA",也就是ServiceDescriptor里面的ServiceType+新增的ServiceKey

public class ServiceDescriptor
{
    public object? ServiceKey { get; } 
    public Type? ImplementationType => _implementationType;        
    public Type ServiceType { get; }
    public ServiceLifetime Lifetime { get; }

对于以前注册的类,ServiceKey默认是null,所以“标识”就是ServiceKey+null。调用GetService<IFoo>()就等于调用GetKeyedService<IFoo>(null)

再举一个例子:

//类型的注册信息放在_descriptorLookup,8.0前,是通过ServiceType作为字典的键,
//8.0是把ServiceIdentifier(也就是ServiceKey+ServiceType)作为字典的键
//7.0
private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new();
//8.0
private readonly Dictionary<ServiceIdentifier, ServiceDescriptorCacheItem> _descriptorLookup = new();
internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
{
    public object? ServiceKey { get; }
    public Type ServiceType { get; }
}

循环引用

前面讲到可以通过[ServiceKey]获取调用时的Key;而没有注册key的服务是无法在构造函数中注入key的值。通过这个功能可以解决循环引用的问题,先看代码。

class Foo : IFoo
{
    //这个构造函数给GetService<IFoo>()使用
    public Foo()
    {
        this.Num = 10;
    }
    //这个构造函数给GetKeyedServices<IFoo>("keyA")使用
    public Foo([ServiceKey] string key, IFoo foo)
    {
        Console.WriteLine($"key:{key},this.Num:{this.Num},foo.Num:{foo.Num}");
    }
    public int Num { get; set; }
}

代码执行流程:

  • 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<IFoo>("keyA"),是使用的这个构造函数Foo([ServiceKey] string key, IFoo foo)创建的对象。IFoo foo是使用无参构造函数创建的。

注意点:

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

总结

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

标签:8.0,Denpendcy,ServiceKey,IFoo,key,Injection,foo,public,构造函数
From: https://www.cnblogs.com/tenleft/p/17719609.html

相关文章

  • 升级UBUNTU 18.04时出现DPKG错误
    报错:dpkg:errorprocessingpackageca-certificates-uniontech(--configure):解决:sudodpkg--configure-a  参考:升级UBUNTU18.04时出现DPKG错误 ......
  • 15年磨砺,亚信科技AntDB 8.0数据库,倾“擎”发布
    ​​关于AntDB数据库AntDB数据库始于2008年,在运营商的核心系统上,为全国24个省份的10亿多用户提供在线服务,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行近十年,并在通信、金融、交通、能源、物联网等行业成功商用落地。......
  • Linux系统同时安装MySQL5.7和MySQL8.0
    本文是在一台Centos7虚拟机上面同时安装mysql5.7和mysql8.0的步骤,记录一下,方便后续回顾,这篇文章之后会接着学习搭建两台虚拟机一主一从的架构。其中配置的文件名称、目录、端口号、IP地址要根据自己电脑的实际情况进行更改。mysql5.7和mysql8.0同时安装完成后:将安装包上传到家目录......
  • DVWA靶场通关- SQL Injection(SQL注入)
    BruteForce(暴力(破解))、CommandInjection(命令行注入)、CSRF(跨站请求伪造)、     FileInclusion(文件包含)、FileUpload(文件上传)、InsecureCAPTCHA(不安全的验证码)、    SQLInjection(SQL注入)、SQLInjection(Blind)(SQL盲注)、XSS(DOM)(基于DOM树)、    XSS(Reflec......
  • Linux安装MySQL(8.0)
    Linux安装MySQL(8.0)​ 下载安装包,官网地址:http://dev.mysql.com/downloads/mysql/​ 将安装包上传至服务器并解压,eg:tar-zxvfmysql-8.0.34-linux-glibc2.17-x86_64.tar.gz​ 将解压后的文件夹移动到/usr/local/mysql路径下并重命名,eg:mvmysql-8.0.34-linux-glibc2.17-x86_......
  • win10下docker安装 ubuntu18.04.
      参考 Win10使用Docker安装Ubuntu环境-知乎(zhihu.com)旧版WSL的手动安装步骤|MicrosoftLearn......
  • MySQL 8.0 OCP 最新中文考试题库(如需完整版请联系作者)
    大家好!今天要给大家带来的是由Oracle公司研发的MySQL8.0认证考试试题本次试题是全网最全面的试题,总共包含123道。试题正确率在95%以上。对于在今年报考MySQL8.0中文版本的考生有很大的帮助。特别是考试时所遇到的题型,几乎是原题,本人刚考过,特来为大家分享49.使用带有新配置的旧......
  • MySQL 8.0 OCP 最新中文考试题库(如需完整版请联系作者)
    大家好!今天要给大家带来的是由Oracle公司研发的MySQL8.0认证考试试题本次试题是全网最全面的试题,总共包含123道。试题正确率在95%以上。对于在今年报考MySQL8.0中文版本的考生有很大的帮助。特别是考试时所遇到的题型,几乎是原题,本人刚考过,特来为大家分享49.使用带有新配置的旧......
  • Ubuntu 18.0 vscode 配置 C环境
    sudoapt-getupdatesudoaptinstallgccsudoaptinstallgdbsudoaptinstallclangsudoaptinstalllldb c_cpp_properties.json{"configurations":[{"name":"linux-gcc-x64","includePath":[......
  • DVWA靶场通关-Command Injection(命令行注入)
    BruteForce(暴力(破解))、CommandInjection(命令行注入)、CSRF(跨站请求伪造)、     FileInclusion(文件包含)、FileUpload(文件上传)、InsecureCAPTCHA(不安全的验证码)、    SQLInjection(SQL注入)、SQLInjection(Blind)(SQL盲注)、XSS(DOM)(基于DOM树)、    XSS(Reflec......