一、依赖注入基础概念
1.1 什么是依赖注入(DI)
依赖注入(Dependency Injection)是一种实现控制反转(IoC)的设计模式,它将对象的创建和绑定从使用对象的地方解耦。在C#中,依赖注入已成为现代应用程序开发的核心模式。
1.2 依赖注入的三种方式
- 构造函数注入:最常用的方式,通过构造函数传递依赖
- 属性注入:通过公共属性设置依赖
- 方法注入:通过特定方法传递依赖
二、.NET Core内置DI容器
2.1 服务注册基础
// 在Startup.cs或Program.cs中配置
services.AddTransient<IMyService, MyService>(); // 每次请求创建新实例
services.AddScoped<IMyRepository, MyRepository>(); // 每个请求范围一个实例
services.AddSingleton<ICacheService, CacheService>(); // 全局单例
2.2 生命周期管理
生命周期 | 描述 | 适用场景 |
---|---|---|
Transient | 每次请求创建新实例 | 轻量级、无状态服务 |
Scoped | 每个请求范围一个实例 | 数据库上下文、请求相关服务 |
Singleton | 整个应用生命周期一个实例 | 配置服务、缓存服务 |
三、高级注册技巧
3.1 多接口实现注册
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<IMessageService, SmsService>();
// 获取所有实现
var services = provider.GetServices<IMessageService>();
3.2 泛型服务注册
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
3.3 委托工厂注册
services.AddTransient<IService>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
return new Service(config.GetValue<string>("Setting"));
});
四、构造函数注入实践
4.1 基本用法
public class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
public void ProcessOrder(Order order)
{
_repository.Save(order);
}
}
4.2 多依赖处理
public class ReportGenerator
{
private readonly IDataService _dataService;
private readonly IFormatter _formatter;
public ReportGenerator(IDataService dataService, IFormatter formatter)
{
_dataService = dataService;
_formatter = formatter;
}
}
五、属性与方法注入
5.1 属性注入实现
public class NotificationService
{
[FromServices] // 需要Microsoft.AspNetCore.Mvc.Core包
public IMessageSender MessageSender { get; set; }
}
5.2 方法注入示例
public class DataProcessor
{
public void Process([FromServices] IValidator validator)
{
validator.Validate();
}
}
六、第三方DI容器集成
6.1 Autofac集成
// Program.cs
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new MyApplicationModule());
});
// 模块定义
public class MyApplicationModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<MyService>().As<IMyService>();
}
}
6.2 Simple Injector集成
var container = new Container();
container.Register<IMyService, MyService>(Lifestyle.Scoped);
container.Verify();
services.AddSimpleInjector(container);
七、DI在ASP.NET Core中的应用
7.1 控制器注入
[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
private readonly IWeatherService _weatherService;
public WeatherController(IWeatherService weatherService)
{
_weatherService = weatherService;
}
[HttpGet]
public ActionResult Get()
{
return Ok(_weatherService.GetForecast());
}
}
7.2 视图中的服务注入
@inject IGreeter Greeter
<h1>@Greeter.GetGreeting()</h1>
八、高级应用场景
8.1 装饰器模式实现
services.AddTransient<IDataService, DataService>();
services.Decorate<IDataService, LoggingDataService>();
8.2 条件注册
services.AddTransient<IMessageService>(provider =>
{
var env = provider.GetRequiredService<IWebHostEnvironment>();
return env.IsDevelopment()
? new DebugMessageService()
: new ProductionMessageService();
});
8.3 动态解析
public class ProcessorFactory
{
private readonly IServiceProvider _provider;
public ProcessorFactory(IServiceProvider provider)
{
_provider = provider;
}
public IProcessor GetProcessor(string type)
{
return type switch
{
"A" => _provider.GetRequiredService<ProcessorA>(),
"B" => _provider.GetRequiredService<ProcessorB>(),
_ => throw new ArgumentException("Invalid processor type")
};
}
}
九、DI最佳实践
- 构造函数注入优先:保持代码清晰可测试
- 避免服务定位器模式:减少对ServiceProvider的直接使用
- 合理使用生命周期:根据服务特性选择适当生命周期
- 避免循环依赖:设计扁平化的依赖关系
- 接口抽象:依赖抽象而非具体实现
- 最小化构造函数参数:保持类的单一职责
十、常见问题解决方案
10.1 循环依赖问题
// 重构为:
public class ServiceA
{
private readonly IServiceB _b;
public ServiceA(IServiceB b) { _b = b; }
}
public class ServiceB
{
private readonly Lazy<IServiceA> _a;
public ServiceB(Lazy<IServiceA> a) { _a = a; }
}
10.2 多实现选择
services.AddTransient<PrimaryService>();
services.AddTransient<SecondaryService>();
services.AddTransient<IService>(provider =>
provider.GetRequiredService<PrimaryService>());
10.3 配置选项注入
services.Configure<MyOptions>(Configuration.GetSection("MySettings"));
public class MyService
{
private readonly MyOptions _options;
public MyService(IOptions<MyOptions> options)
{
_options = options.Value;
}
}
十一、性能优化技巧
- Singleton服务谨慎使用:避免内存泄漏
- 避免在Singleton中使用Scoped服务:会导致Scoped服务变为Singleton
- 使用TryAdd系列方法:防止重复注册
- 延迟初始化:对昂贵资源使用Lazy
- 池化技术:对高开销对象使用对象池
十二、单元测试中的DI
12.1 使用Moq模拟依赖
[Fact]
public void TestOrderProcessing()
{
// Arrange
var mockRepo = new Mock<IOrderRepository>();
var service = new OrderService(mockRepo.Object);
// Act
service.ProcessOrder(new Order());
// Assert
mockRepo.Verify(r => r.Save(It.IsAny<Order>()), Times.Once);
}
12.2 使用测试容器
var services = new ServiceCollection();
services.AddTransient<IMyService, TestMyService>();
var provider = services.BuildServiceProvider();
var service = provider.GetRequiredService<IMyService>();
十三、总结
C#中的依赖注入是现代应用程序开发的核心技术,.NET Core内置的DI容器提供了强大而灵活的功能。通过合理应用DI,可以显著提高代码的可测试性、可维护性和可扩展性。掌握DI的各种实现方式和最佳实践,是成为高级C#开发者的必备技能。