ASP.NET Core 学习(2)
ASP.NET Core、c#、依赖注入
正文内容
依赖注入
当程序中存在大量类型时,用于自动管理类型之间的依赖关系,当调用方需要某个类型的实例时,该机制可以自动创建该类型的实例并完成注入。
该机制包括两部分:
- 一个容器,即服务容器,可以将存在依赖关系的类型添加到该容器中。
- 自动激活实例,当调用者需要容器中某个类型的实例时,服务容器会自动调用类型的构造函数创建实例,并根据构造函数的参数列表自动创建和引用所依赖的实例类型。
1、ServiceCollection类
ServiceCollection类是.NET内置的服务容器,它实现了IServiceCollection接口,使用方法与其他泛型集合相同。
主要属性:
ServiceType:表示服务类型的的Type对象。ImplementationType(可选):如果服务的实现方式为“接口+实现类”或“抽象类+派生类”,那么ServiceType属性表示服务的接口或抽象类,ImplementationType表示服务的实现类。ImplementationInstance:表示服务类型的实例对象。Lifetime:表示服务类型的生存期。
主要扩展方法:
add()、TryAdd():将单个或多个服务添加到服务容器中。Replace():替换容器中的ServiceDescriptor实例。RemoveAll():删除容器中所有与指定类型相同的服务。AddSingleton()、TryAddSingleton():将服务添加到容器中,且服务实例的生存期为单个实例AddTransient、TryAddTransient():将服务添加到容器中,且服务实例的生存期为瞬时。AddScoped()、TryAddScoped():将服务添加到容器中,且服务实例的生存期被限定为“作用域”范围内。
以TryAdd开头命名的方法表示只有当目标服务类型未在容器中注册时才会添加服务,否则不进行任何处理。
关于服务的生存期:
- 单例服务:生存期最长,实例生存期与服务容器相同。整个容器的生存期内,服务仅创建一个实例。
- 瞬时服务:生存期最短,每次从服务容器中获取时都会创建新的服务实例。
- 作用域服务:从容器根部的ServiceProvider上创建子作用域,在该作用域的生存期内,服务只创建一个实例。当该作用域的生命周期结束时会释放服务实例。
对于作用域服务,需注意在默认情况下是不能直接通过ServiceProvider对象获取实例,只能先调用CreateScope()或CreateAsyncScope()来创建子作用域,然后通过子作用域中的ServiceProvider对象来获取实例。
using var rootProvider = services.BuildServiceProvider();
using(var scoped = rootProvider.CreateScope())
{
var v1 = scoped.ServiceProvider.GetRequiredService<TestService>();
}
若确实需要从根部获取作用域服务的实例,可以在调用BuildServiceProvider时将validateScopes参数设置为false即可,如:
using var rootProvider = services.BuildServiceProvider(validateScopes : false);
2、ServiceProvider类
当服务类型注册到容器后,可使用ServiceProvider类获取服务类型的实例。ServiceProvider类没有公共构造函数,无法实例化,应调用IServiceCollection类的BuildServiceProvider()获得ServiceProvider实例。
最佳实践为使用IServiceProvider接口类型来接收ServiceProvider实例,因为有多个与其相关的扩展方法均面向IServiceProvider接口。
3、.NET项目中的依赖注入
非ASP.NET Core项目在默认情况下不包含与依赖注入的相关引用,需要手动添加Microsoft.Extensions.DependencyInjection引用,然后在对应的代码文件中using即可。
using Microsoft.Extensions.DependencyInjection;
IServiceCollection services = new ServiceCollection();
//添加服务
services.AddTransient<ITestService, MyService>();
//构建Provider
IServiceProvider serviceProvider = services.BuildServiceProvider();
获取对象时
//返回指定服务类型的实例,若指定服务类型不存在,返回null
ITestService? service1 = serviceProvider.GetService<ITestService>();
//或使用非泛型GetService
ITestService? service2 = serviceProvider.GetService(typeof(ITestService)) as ITestService;
4、ASP.NET Core项目中的依赖注入
ASP.NET Core项目在默认情况下已经包含与依赖注入的相关引用,无需手动添加
//调用时已自动创建服务容器ServiceCollection
//后续可通过Services属性访问ServiceCollection
var builder = WebApplication.CreateBuilder(args);
...
...
...
//待WebApplication对象构建完成后,服务容器将变为只读状态,不能修改
var app = builder.Build();
后续使用GetService()来获取要使用的服务实例
//获取服务实例
ILoggerFactory? logfac = app.Services.GetService<ILoggerFactory>();
5、GetService()与GetRequiredService()对比
两者都可以从Services中获取服务实例,区别如下:
- GetService():如果服务已经在容器中注册,则返回服务实例,否则返回null,不会引发异常,代码中需要用户进行null检查。
- GetRequiredService():如果服务已经在容器中注册,则返回服务实例,否则会引发
InvalidOperationException异常。
6、注入多个服务实例
在获取服务实例时,优先获取最后添加到容器中的类型,例如:
builder.Services.AddSingleton<ITestService, MyService1>();
builder.Services.AddSingleton<ITestService, MyService2>();
...
//最终获取到的是MyService2
ITestService? service = app.Services.GetService<ITestService>();
在有些特殊场合,开发人员希望同时获取MyService1与MyService2的实例,此时应该调用GetServices()方法,此方法返回一个服务实例列表,其中包含已注册的ITestService中的所有实例
IEnumerable<ITestService> allServices = app.Services.GetServices<ITestService>();
foreach(var s in allServices)
{
//...
}
7、由于依赖关系产生的生存周期变化问题
若Service1类被注册为瞬时服务,Service2被注册为单例服务,Service2类需要引用Service1类,即Service1类的实例会被注入到Service2类中。此时由于依赖关系,Service1的生存周期被延长了——从瞬时服务变为了单例服务。因此,在处理服务类的依赖关系时,应避免将生存期短的服务注入到生存期较长的服务中。
正文内容已启用复制保护,代码块仍支持复制。
评论区
0 条已展示评论发表评论
提交后将进入人工审核,审核通过后才会展示。
读者留言
以下仅展示已通过审核的评论内容。