Something beautiful is on the way.

.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. 生命周期管理最佳实践

  1. 作用域服务(Scoped)

    • 适用于大多数场景(如DbContext、Repository)。
    • 在同一个HTTP请求内共享实例。
  2. 单例服务(Singleton)

    • 用于无状态服务(如配置、日志)。
    • 避免注入Scoped服务到Singleton中(会导致Captive Dependency问题)。
  3. 瞬时服务(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> + 工厂 灵活控制 多策略支付、验证

通过合理使用依赖注入,可以显著提升代码的可测试性和可维护性。

posted @ 2025-05-29 19:08  张朋举  阅读(133)  评论(0)    收藏  举报