.NET WebAPI 中,依赖注入
在 .NET WebAPI 中,依赖注入(Dependency Injection, DI)是管理服务生命周期的核心机制。以下是关于 如何注入服务 的详细指南,涵盖基础注入、高级场景和最佳实践:
1. 基础服务注入
1.1 注册服务(Program.cs)
var builder = WebApplication.CreateBuilder(args);
// 1. 单例(Singleton): 整个应用生命周期共享一个实例
builder.Services.AddSingleton<IMySingletonService, MySingletonService>();
// 2. 作用域(Scoped): 每个HTTP请求一个实例(最常用)
builder.Services.AddScoped<IMyScopedService, MyScopedService>();
// 3. 瞬时(Transient): 每次请求创建一个新实例
builder.Services.AddTransient<IMyTransientService, MyTransientService>();
// 4. 注入HttpClient(命名客户端)
builder.Services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});
1.2 在控制器中使用
[ApiController]
[Route("api/[controller]")]
public class MyController : ControllerBase
{
private readonly IMyScopedService _service;
private readonly IHttpClientFactory _httpClientFactory;
// 通过构造函数注入
public MyController(
IMyScopedService service,
IHttpClientFactory httpClientFactory)
{
_service = service;
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public IActionResult Get()
{
var data = _service.GetData();
return Ok(data);
}
}
2. 注入配置(IConfiguration)
2.1 读取appsettings.json
// appsettings.json
{
"Jwt": {
"Secret": "my-secret-key",
"ExpiryMinutes": 60
}
}
2.2 注入并使用配置
// 注册自定义配置类(可选)
builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("Jwt"));
// 在服务中注入IConfiguration
public class AuthService
{
private readonly IConfiguration _config;
public AuthService(IConfiguration config) => _config = config;
public string GenerateToken()
{
var secret = _config["Jwt:Secret"];
var expiry = _config.GetValue<int>("Jwt:ExpiryMinutes");
// ...
}
}
3. 高级注入场景
3.1 注入多个实现(策略模式)
// 注册多个实现
builder.Services.AddScoped<IPaymentService, CreditCardPaymentService>();
builder.Services.AddScoped<IPaymentService, PayPalPaymentService>();
// 通过IEnumerable<T>获取所有实现
public class OrderController : ControllerBase
{
private readonly IEnumerable<IPaymentService> _paymentServices;
public OrderController(IEnumerable<IPaymentService> paymentServices)
=> _paymentServices = paymentServices;
[HttpPost("pay")]
public IActionResult Pay(string paymentMethod)
{
var service = _paymentServices.FirstOrDefault(s => s.CanHandle(paymentMethod));
service?.ProcessPayment();
return Ok();
}
}
3.2 条件注入
// 根据环境变量选择实现
if (builder.Environment.IsDevelopment())
{
builder.Services.AddScoped<IDataService, MockDataService>();
}
else
{
builder.Services.AddScoped<IDataService, RealDataService>();
}
3.3 工厂模式注入
// 注册带参数的工厂
builder.Services.AddScoped<IService>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
return new Service(config["ApiKey"]);
});
4. 第三方库集成
4.1 使用AutoMapper
// 安装包:AutoMapper.Extensions.DependencyInjection
builder.Services.AddAutoMapper(typeof(Program).Assembly);
// 在服务中注入IMapper
public class UserController : ControllerBase
{
private readonly IMapper _mapper;
public UserController(IMapper mapper) => _mapper = mapper;
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
var user = _userRepository.GetById(id);
var dto = _mapper.Map<UserDto>(user);
return Ok(dto);
}
}
4.2 使用MediatR(CQRS)
// 安装包:MediatR.Extensions.Microsoft.DependencyInjection
builder.Services.AddMediatR(typeof(Program).Assembly);
// 定义Handler
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, UserDto>
{
public Task<UserDto> Handle(GetUserQuery request, CancellationToken ct)
{
// ...
}
}
// 在控制器中使用
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator) => _mediator = mediator;
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var user = await _mediator.Send(new GetUserQuery(id));
return Ok(user);
}
}
5. 生命周期管理最佳实践
-
作用域服务(Scoped):
- 适用于大多数场景(如DbContext、Repository)。
- 在同一个HTTP请求内共享实例。
-
单例服务(Singleton):
- 用于无状态服务(如配置、日志)。
- 避免注入Scoped服务到Singleton中(会导致Captive Dependency问题)。
-
瞬时服务(Transient):
- 轻量级、无状态服务(如DTO映射器)。
- 每次请求创建新实例,性能开销稍大。
6. 常见问题解决
Q:如何解决循环依赖?
// 错误:A依赖B,B又依赖A
// 解决方案:引入第三个服务C,或使用懒加载(Lazy<T>)
public class ServiceA(IServiceB b) { /* ... */ }
public class ServiceB(Lazy<IServiceA> a) { /* ... */ }
Q:如何手动解析服务?
// 在非控制器类中获取服务
public class MyUtility
{
private readonly IServiceProvider _provider;
public MyUtility(IServiceProvider provider) => _provider = provider;
public void DoWork()
{
using var scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IMyService>();
service.Execute();
}
}
总结
| 场景 | 注册方法 | 生命周期 | 典型用例 |
|---|---|---|---|
| 全局共享 | AddSingleton |
应用级别 | 配置、日志、缓存 |
| 请求内共享 | AddScoped |
请求级别 | DbContext、Repository |
| 每次创建 | AddTransient |
每次解析 | 轻量级工具类 |
| 动态选择实现 | IEnumerable<T> + 工厂 |
灵活控制 | 多策略支付、验证 |
通过合理使用依赖注入,可以显著提升代码的可测试性和可维护性。
浙公网安备 33010602011771号