Denpendcy Injection 8.0新功能——KeyedService

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就可以获取对象。暂时不支持使用通配符匹配,也许以后会加......

csharp 复制代码
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

csharp 复制代码
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)

再举一个例子:

csharp 复制代码
//类型的注册信息放在_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的值。通过这个功能可以解决循环引用的问题,先看代码。

csharp 复制代码
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就和以前的用法一模一样。

结束。第一次写文章如有错误,欢迎各位批评指点,谢谢!

相关推荐
棉晗榜7 天前
.net core在linux导出excel,System.Drawing.Common is not supported on this platform
linux·excel·asp.net core·miniexcel
棉晗榜7 天前
asp.net core发布配置端口号,支持linux
asp.net core
coredx9 天前
如何优雅地让 ASP.NET Core 支持异步模型验证
asp.net core
小乖兽技术17 天前
ASP.NET Core Web 项目的部署:选择 IIS 还是 Kestrel?
后端·kestrel·iis·asp.net·asp.net core
界面开发小八哥19 天前
DevExtreme JS & ASP.NET Core v24.2新功能预览 - 全新的聊天组件
javascript·ui·asp.net core·界面控件·ui开发·devextreme·.net 9
gc_229924 天前
学习ASP.NET Core的身份认证(基于Session的身份认证3)
asp.net core·session·身份认证
gc_229925 天前
ASP.NET Core项目中使用SqlSugar连接多个数据库的方式
asp.net core·sqlsugar·多数据库
gc_22991 个月前
学习ASP.NET Core的身份认证(基于Session的身份认证1)
asp.net core·session·身份认证
gc_22991 个月前
学习ASP.NET Core的身份认证(基于Cookie的身份认证1)
asp.net core·cookie·身份认证
gc_22991 个月前
学习ASP.NET Core的身份认证(基于Cookie的身份认证3)
asp.net core·cookie·身份认证