依赖注入
一、为什么要实现依赖注入?
第一:可以轻松管理类之间的依赖,帮助在构建应用时遵循设计原则,确保代码的可维护性和可扩展性。
第二:ASP.NET Core 的整个架构中,依赖注入框架提供了对象创建和生命周期管理的
核心能力,各个组件相互协作,也是由依赖注入框架的能力来实现的。
二、生命周期
1.单例 Singleton
同一个类型就一个实例。
代码如下(示例):
services.AddSingleton<IService>(new ClientService());//直接注入实例
services.AddSingleton<IService, ClientServiceExt>();
2.作用域 Scoped
在同一个作用域内是同一个实例,典型的场景是EFDBContext对象。同一个作用域共用同一个DBContext对象。
代码如下(示例):
services.AddScoped<IService>(serviceProvider =>
{
return new ClientServiceExt();
});
3.瞬时(暂时) Transient
每一次获取都是不同的实例。
代码如下(示例):
services.AddTransient<ITransientService, TransientService>();
使用场景总结:
对于方法都是线程安全的类型,一般使用单例。
对于对象依赖了处理上下文,或者使用了有限资源(如数据库连接),一般使用Scoped或者Transient。
Scoped、Transient的区别是你在同一个上下文中是否期望使用同一个实例,如果是,用Scoped,反之则使用Transient
三、注册方式
1.花式注册
代码如下(示例):
services.AddSingleton<IService>(new ClientService());//直接注入实例
services.AddSingleton<IService, ClientServiceExt>();
//services.AddSingleton<IService, ClientService>();
//services.AddSingleton<IService>(serviceProvider => {
// //serviceProvider.GetService();可以聚合复杂的实例
// return new ClientService();
//});
services.AddScoped<IService>(serviceProvider =>
{
return new ClientServiceExt();
});
2.尝试注册
若已经注册过的类型实例则不再注册。
代码如下(示例):
//services.TryAddSingleton<IService, ClientService>();
//services.TryAddSingleton<IService, ClientServiceExt>();
//services.TryAddEnumerable(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
//services.TryAddEnumerable(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
//services.TryAddEnumerable(ServiceDescriptor.Singleton<IService>(new ClientServiceExt()));
//services.TryAddEnumerable(ServiceDescriptor.Singleton<IService>(serviceProvider =>
//{
// return new ClientServiceExt();
//}));
//services.TryAddEnumerable(ServiceDescriptor.Transient<IService>(serviceProvider =>
//{
// return new ClientServiceExt();
//}));
//services.TryAddEnumerable(ServiceDescriptor.Scoped<IService>(serviceProvider =>
//{
// return new ClientServiceExt();
//}));
3.注册泛型模板
代码如下(示例):
services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>));
4.Remove&Replace
Replace:先删除匹配的第一个,然后再末尾追加注册新的实例。
Remove:移除某个服务类型的所有注册。
代码如下(示例):
services.Replace(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
services.RemoveAll<IService>();
四、实例获取方式
1.通过标注FromServicesAttribute
FromServicesAttribute是一个mvc的模型绑定描述类,因此只在controller的action参数上生效。
代码如下(示例):
[HttpGet]
public int PrintService([FromServices]ISingletonService singletonService1, [FromServices]ISingletonService singletonService2
, [FromServices]ITransientService transientService1, [FromServices]ITransientService transientService2
, [FromServices]IScopedService scopedService1, [FromServices]IScopedService scopedService2
)
{
Debug.WriteLine($"singletonService1's hashcode ={singletonService1.GetHashCode()}");
Debug.WriteLine($"singletonService2's hashcode ={singletonService2.GetHashCode()}");
Debug.WriteLine($"transientService1's hashcode ={transientService1.GetHashCode()}");
Debug.WriteLine($"transientService2's hashcode ={transientService2.GetHashCode()}");
Debug.WriteLine($"scopedService1's hashcode ={scopedService1.GetHashCode()}");
Debug.WriteLine($"scopedService2's hashcode ={scopedService2.GetHashCode()}");
return 0;
}
2.通过构造函数
代码如下(示例):
private readonly ILogger<WeatherForecastController> _logger;
private readonly IGenericService<IService> _genericService;
public WeatherForecastController(ILogger<WeatherForecastController> logger,IGenericService<IService> genericService)
{
_logger = logger;
_genericService = genericService;
}
这二种方式的区别:全局使用则通过构造函数来获取,单个服务使用则通过FromServices标注来获取。
五、源码下载
总结
- 避免通过静态属性的方式访问容器对象
- 避免在服务内部使用 GetService 方式来获取实例
- 避免使用静态属性存储单例,应该使用容器管理单例对象
- 避免在服务中实例化依赖对象,应该使用依赖注入来获得依赖对象
- 避免向单例的类型注入范围的类型

本文详细介绍了ASP.NET Core中依赖注入的重要性,包括管理类依赖、对象生命周期(单例、作用域、瞬时)和注册方式(花式注册、尝试注册、泛型模板、Remove&Replace)。同时,讨论了实例获取的两种方式:通过FromServicesAttribute标注和构造函数,并强调了依赖注入的最佳实践,提醒开发者避免不恰当的容器使用和依赖管理。
503

被折叠的 条评论
为什么被折叠?



