知识点回顾
依赖包. Microsoft.Extensions.DependencyInjection.Abstractions
核心对象和方法.
IServiceCollection. 注入对象的容器. 注意它只存储对象的元数据, 并不保存实例对象.
IServiceProvider. 注入对象的提供者. 它负责提供具体的对象实例. 在架构中, IServiceProvider 有 2 种身份, 一种是 Root ServiceProvider, 由 service.BuildServiceProvider()创建, 生命周期贯穿整个应用程序, AddSingleton 对象保存在这里. 另外一种则是普通 IServiceProvider, 由 IServiceScope 创建, 生命周期即为 AddScoped 的生命周期. AddScope 的对象保存在这里. 普通 ServiceProvider 由 Root ServiceProvider 创建的 IServiceScope 创建.
IServiceScope. 表某一个生命周期范围. 由 ServiceProvider.CreateScope()创建.
注入方式, 知识点一.
注入功能默认在 Startup 类中的 ConfigureServices 方法中.
注入方式, 知识点二. 支持以下三种方式
AddScoped. 生命周期为 Scoped 类型. 例如在 web 框架中, 即表示一次 Request 请求范围内.
AddSingleton. 单例, 应用程序全局使用同一个实例.
AddTransient. 即时的, 即对象每次使用都会重新实例化.
注入方式, 知识点三. 提供多种注入技巧, 以 Transient 为例
实例注入. AddTransient<TService>(this IServiceCollection services).
泛型注入. AddTransient<TService, TImplementation>(this IServiceCollection services).
工厂注入. AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory).
TryAddXXX. 仅当 XXX 尚未注册实现时, 注册该服务. 此方法用来避免在容器中注册一个实例的两个副本.
获取实例的方法 GetService 和 GetRequiredService 的区别, 前置如果 service 不存在会返回 NULL, 后者会直接抛出异常. 根据需要选择 GetRequiredService, 可能会让你的代码变得简洁一点.
WHY 依赖注入
这里只谈益处.
使用接口或基类抽象化依赖关系实现, 明确各个类之间的依赖关系.
生命周期的统一管理, 尤其对于某些类被多处依赖, 关系会变得分散难以管理, 依赖注入容器可以解决这点.
非常利于单元测试.
最佳实践
部分来自官方文档的一些建议
对于需要注入为单例的实例, 不要依赖 Scoped 实例. 会触发 .NET CORE 作用域验证失败.
不要从 Root IServiceProvider 解析有作用域的实例, 这样会导致该作用域的实例变成单一实例. 同样会触发作用域验证失败.
对于 ASP.NET Core, 尽量通过构造函数而不是 HttpContext.RequestServices 获取实例, 这样更易于单元测试.
需要对某个组件服务或是一些服务集合(包括其依赖注入时), 使用约定的 Add{SERVICE_NAME} 扩展方法来注册该服务所需的所有服务.
若必须要从 IServiceProvider 中解析实例 (如在单元测试中), 请通过 using (var scope = ServiceProvider.CreateScope()){ } 方式创建作用域来获取实例.
代码中避免设计有状态的, 静态类和成员. 可以考虑设计注入为单一实例.
代码中避免在服务中直接实例化以来类. 尽量采用依赖注入的方式
注意以下两种方式注入的区别, 后者的实例化不是服务容器创建的, 所有容器不会处理实例的 Dispose !!
- public class Service1 : IDisposable {}
- public class Service2 : IDisposable {}
- // 方式一
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddSingleton<Service1>();
- services.AddSingleton<IService2>(sp => new Service2());
- }
- // 方式二
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddSingleton<Service1>(new Service1());
- services.AddSingleton(new Service2());
- }
延伸上一点, 对于复杂对象的创建, 尽量采用提供的工厂注入方式. 注意工厂注入的参数是 IServiceProvider, 可以通过它获取你需要的实例对象.
继续延伸上一点, 不要在 ConfigureServices 方法中 通过 collection.BuildServiceProvider()来获取 IServiceProvider. 这个创建的是一个 Root IServiceProvider. 单例会实例化一次, 然后 ConfigureServices 方法结束后框架会再次调用 collection.BuildServiceProvider(), 单例又会重新实例化一次.
不支持基于 async/await 和 Task 的服务解析. C# 不支持异步构造函数; 因此建议模式是在同步解析服务后使用异步方法
避免在容器中直接存储数据和配置. 配置应使用 NET CORE 的选项模型.
避免使用服务定位器模式. 例如直接注入 IServiceProvider 来获取多个需要的服务. PS, 如果你的服务依赖项过多, 应该考虑分割成几个小功能服务了.
引入第三方 IoC 框架
.NET CORE 3.x 版本后, 引入第三方 IoC 框架的方式变更了, 这里不再贴出 2.x 的方式. 以 Autofac 框架为例.
Program.cs
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .UseServiceProviderFactory(new AutofacServiceProviderFactory())
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup<Startup>()
- .ConfigureLogging((hostingContext, logging) =>
- {
- logging.ClearProviders();
- logging.AddConsole();
- logging.AddNLog();
- });
- });
Startup.cs
- // 原来的 ConfigureServices 保留, 也可以使用原来的框架继续注入
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddControllers();
- services.AddMemoryCache();
- services.Configure<List<string>>(Configuration.GetSection("BlackPhoneList"));
- services.Configure<Dictionary<string, string>>(Configuration.GetSection("BusinessMessages"));
- }
- // 增加 ConfigureContainer(ContainerBuilder builder) 方式, 使用 Autofac 框架注入
- public void ConfigureContainer(ContainerBuilder builder)
- {
- builder.RegisterType<PhoneBlackListValidator>().Named<IPhoneValidator>("PHONE_BLACKLIST").SingleInstance();
- builder.RegisterType<PhonePerDayCountValidator>().Named<IPhoneValidator>("PHONE_PERDAYCOUNT").SingleInstance();
- builder.RegisterType<UniqueIdPerDayCountValidator>().Named<IUniqueIdValidator>("UNIQUEID_PERDAYCOUNT").SingleInstance();
- // 可遍历类型注入, 注意 只支持 IEnumerable\IList\ICollection 类型
- builder.RegisterType<MessageSendValidator>().As<IMessageSendValidator>().SingleInstance();
- }
3.x 主要是在 IServiceCollection 和 IServiceProvider 之间增加了一个 ContainerBuilder 容器适配, 使得第三方 IoC 框架引入更加合理了. 具体实现原理可以网上源码查找.
特别关注 - 线程安全
创建线程安全的单一实例服务. 如果单例服务依赖于一个 Transient 服务, 那么 Transient 服务可能也需要线程安全, 具体取决于单例使用它的方式.
工厂注入方式的 Func<IServiceProvider,TService > 不需要是线程安全的, 框架保证它由单个线程调用一次.
来源: https://www.cnblogs.com/gt1987/p/12973001.html