在 ASP.NET Core 中,IServiceProvider 是依赖注入的核心接口,用于解析服务实例。可以通过多种方式获取 IServiceProvider,但每种方式的适用场景和行为可能有所不同。下面将详细介绍几种常见的获取方式,并分析它们之间的区别。
创建依赖
//1. 定义服务接口和实现类
public interface IDateTime
{
DateTime Now { get; }
}
public class SystemDateTime : IDateTime
{
public DateTime Now => DateTime.Now;
}
//2.注入服务
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
//Net分享,欢迎关注!
1. 通过构造函数注入
这是最常见的方式之一,适用于需要在整个类中使用 IServiceProvider 的场景。
public class WeatherForecastController : ControllerBase
{
private readonly IServiceProvider _serviceProvider;
public WeatherForecastController(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
[HttpGet(Name = "GetWeatherForecast")]
public bool Get()
{
var service = _serviceProvider.GetService<IDateTime>();
// 使用服务
Console.WriteLine( "NetShare:"+service.Now);
return true;
}
}
特点:
- o 优点:简单直观,符合依赖注入的设计原则。
- o 缺点:可能会导致对 IServiceProvider 的过度依赖(即服务定位器模式),破坏显式依赖声明的原则。
2. 通过HttpContext.RequestServices
在控制器或中间件中,可以通过
HttpContext.RequestServices 获取当前请求作用域内的 IServiceProvider。
public class WeatherForecastController : ControllerBase
{
private readonly IHttpContextAccessor _httpContextAccessor;
public WeatherForecastController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
[HttpGet(Name = "GetWeatherForecast")]
public bool Get()
{
var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;
var service = serviceProvider.GetService<IDateTime>();
// 使用服务
Console.WriteLine("NetShare:" + service.Now);
return true;
}
}
特点:
- o 优点:适合在 HTTP 请求上下文中使用,确保获取的服务是当前请求作用域内的实例。
- o 缺点:需要注入 IHttpContextAccessor,增加了间接依赖。
//需要注册
builder.Services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
3. 通过IServiceScopeFactory
如果需要手动创建作用域并获取服务,可以使用 IServiceScopeFactory。
public class WeatherForecastController : ControllerBase
{
private readonly IServiceScopeFactory _scopeFactory;
public WeatherForecastController(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
[HttpGet(Name = "GetWeatherForecast")]
public bool Get()
{
using (var scope = _scopeFactory.CreateScope())
{
var serviceProvider = scope.ServiceProvider;
var service = serviceProvider.GetService<IDateTime>();
// 使用服务
Console.WriteLine("NetShare:" + service.Now);
}
return true;
}
}
特点:
- o 优点:适合需要手动控制作用域的场景,例如在后台任务中使用依赖注入。
- o 缺点:需要显式管理作用域的生命周期。
4. 通过WebApplication
WebApplication 是应用启动时创建的全局 IServiceProvider,通常用于获取单例服务。
builder.Services.AddSingleton<IDateTime, SystemDateTime>();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
//Net分享,欢迎关注!
var myService = scope.ServiceProvider.GetRequiredService<IDateTime>();
// 使用服务
Console.WriteLine("NetShare:" + myService.Now);
}
特点:
- o 优点:适用于需要在应用生命周期外访问服务的场景(如程序启动时的初始化逻辑)。
- o 缺点:无法获取与请求相关的服务实例。
总结对比
方式适用场景是否支持请求作用域生命周期管理构造函数注入普通类中使用否外部管理HttpContext.RequestServicesHTTP 请求上下文中使用是自动管理WebApplication应用启动时或全局范围内使用否全局单例IServiceScopeFactory需要手动创建作用域的场景是需要手动管理
注意事项
- 1. 避免滥用服务定位器模式
尽量通过构造函数注入显式声明依赖,而不是通过 IServiceProvider 动态获取服务。这有助于提高代码的可读性和可维护性。 - 2. 正确管理作用域
如果使用 IServiceScopeFactory 创建作用域,请确保及时释放资源,避免内存泄漏。 - 3. 区分全局和请求作用域
在多线程或后台任务中,不要直接使用 HttpContext.RequestServices,因为它可能与当前线程无关。
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏并分享给更多开发者!让我们一起学习,共同进步!