C# 依赖注入-DI容器与IoC控制反转
·
什么是IoC和DI
IoC(控制反转)
控制反转(Inversion of Control)是一种设计原则,将对象创建和依赖关系的控制权从应用程序代码转移到框架或容器。
DI(依赖注入)
依赖注入(Dependency Injection)是实现IoC的一种方式,通过外部注入依赖对象,而不是在类内部创建。
为什么需要DI
传统方式的问题
// ❌ 紧耦合的代码
public class UserService
{
private readonly SqlUserRepository _repository;
private readonly EmailService _emailService;
public UserService()
{
// 直接创建依赖,紧耦合
_repository = new SqlUserRepository();
_emailService = new EmailService();
}
public void RegisterUser(User user)
{
_repository.Add(user);
_emailService.SendWelcomeEmail(user.Email);
}
}
// 问题:
// 1. 难以测试(无法 mock 依赖)
// 2. 难以替换实现(如切换到 MongoRepository)
// 3. 违反单一职责原则(负责创建依赖)
DI方式的优势
// ✅ 使用依赖注入
public interface IUserRepository
{
void Add(User user);
}
public interface IEmailService
{
void SendWelcomeEmail(string email);
}
public class UserService
{
private readonly IUserRepository _repository;
private readonly IEmailService _emailService;
// 通过构造函数注入依赖
public UserService(IUserRepository repository, IEmailService emailService)
{
_repository = repository;
_emailService = emailService;
}
public void RegisterUser(User user)
{
_repository.Add(user);
_emailService.SendWelcomeEmail(user.Email);
}
}
// 优势:
// 1. 易于测试(可以注入 mock 对象)
// 2. 灵活替换实现
// 3. 符合SOLID原则
依赖注入的三种方式
1. 构造函数注入(推荐)
public class OrderService
{
private readonly IOrderRepository _repository;
private readonly ILogger _logger;
// 通过构造函数注入
public OrderService(IOrderRepository repository, ILogger logger)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void CreateOrder(Order order)
{
_logger.Log("Creating order...");
_repository.Add(order);
}
}
// 优势:
// - 依赖明确(从构造函数就能看出)
// - 不可变(依赖不会在对象生命周期中改变)
// - 强制提供依赖(缺少依赖无法创建对象)
2. 属性注入
public class ProductService
{
// 通过属性注入(可选依赖)
public ILogger Logger { get; set; }
public void UpdateProduct(Product product)
{
// 使用可选依赖
Logger?.Log("Updating product...");
// 更新逻辑...
}
}
// 使用场景:
// - 可选依赖
// - 框架限制(如某些MVC控制器)
3. 方法注入
public class ReportService
{
// 通过方法参数注入
public void GenerateReport(IReportGenerator generator, ReportData data)
{
var report = generator.Generate(data);
// 处理报告...
}
}
// 使用场景:
// - 每次调用可能需要不同的依赖
// - 依赖仅在特定方法中使用
Microsoft.Extensions.DependencyInjection
基础使用
using Microsoft.Extensions.DependencyInjection;
// 创建服务容器
var services = new ServiceCollection();
// 注册服务
services.AddTransient<IUserRepository, SqlUserRepository>();
services.AddTransient<IEmailService, EmailService>();
services.AddTransient<UserService>();
// 构建服务提供者
var serviceProvider = services.BuildServiceProvider();
// 获取服务
var userService = serviceProvider.GetService<UserService>();
userService.RegisterUser(new User { Name = "张三" });
服务注册方式
// 1. 泛型注册
services.AddTransient<IUserService, UserService>();
// 2. 基于类型注册
services.AddTransient(typeof(IUserService), typeof(UserService));
// 3. 工厂方法注册
services.AddTransient<IUserService>(provider =>
{
var repository = provider.GetService<IUserRepository>();
return new UserService(repository);
});
// 4. 注册实例
var instance = new UserService(new SqlUserRepository());
services.AddSingleton<IUserService>(instance);
// 5. 注册自身
services.AddTransient<UserService>();
服务生命周期
Transient(瞬时)
// 每次请求都创建新实例
services.AddTransient<ITransientService, TransientService>();
var service1 = provider.GetService<ITransientService>();
var service2 = provider.GetService<ITransientService>();
// service1 != service2(不同实例)
// 使用场景:
// - 轻量级、无状态服务
// - 每次使用都需要新实例的服务
Scoped(作用域)
// 在同一作用域内是同一实例
services.AddScoped<IScopedService, ScopedService>();
using (var scope = provider.CreateScope())
{
var service1 = scope.ServiceProvider.GetService<IScopedService>();
var service2 = scope.ServiceProvider.GetService<IScopedService>();
// service1 == service2(同一实例)
}
using (var scope = provider.CreateScope())
{
var service3 = scope.ServiceProvider.GetService<IScopedService>();
// service3 != service1(不同作用域,不同实例)
}
// 使用场景:
// - Web应用中的请求作用域
// - 数据库上下文(DbContext)
// - 工作单元模式
Singleton(单例)
// 整个应用程序生命周期内只有一个实例
services.AddSingleton<ISingletonService, SingletonService>();
var service1 = provider.GetService<ISingletonService>();
var service2 = provider.GetService<ISingletonService>();
// service1 == service2(同一实例)
// 使用场景:
// - 配置服务
// - 缓存服务
// - 日志服务
// - 需要全局共享的服务
生命周期对比
public class LifetimeDemo
{
public void Demo()
{
var services = new ServiceCollection();
services.AddTransient<TransientService>();
services.AddScoped<ScopedService>();
services.AddSingleton<SingletonService>();
var provider = services.BuildServiceProvider();
// 第一次获取
Console.WriteLine("=== 第一次获取 ===");
var t1 = provider.GetService<TransientService>();
var sc1 = provider.GetService<ScopedService>();
var si1 = provider.GetService<SingletonService>();
// 第二次获取(同一作用域)
Console.WriteLine("=== 第二次获取(同一作用域)===");
var t2 = provider.GetService<TransientService>(); // 新实例
var sc2 = provider.GetService<ScopedService>(); // 同一实例
var si2 = provider.GetService<SingletonService>(); // 同一实例
// 新作用域
Console.WriteLine("=== 新作用域 ===");
using (var scope = provider.CreateScope())
{
var t3 = scope.ServiceProvider.GetService<TransientService>(); // 新实例
var sc3 = scope.ServiceProvider.GetService<ScopedService>(); // 新实例
var si3 = scope.ServiceProvider.GetService<SingletonService>(); // 同一实例
}
}
}
实战场景
场景1:Web API中的DI
// Startup.cs 或 Program.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 数据库上下文(Scoped)
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Default")));
// 仓储(Scoped,与DbContext生命周期一致)
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IOrderRepository, OrderRepository>();
// 业务服务(Scoped)
services.AddScoped<IUserService, UserService>();
services.AddScoped<IOrderService, OrderService>();
// 工具类(Singleton)
services.AddSingleton<IEmailSender, EmailSender>();
services.AddSingleton<ICacheService, RedisCacheService>();
// 日志(Singleton)
services.AddLogging(builder =>
{
builder.AddConsole();
builder.AddDebug();
});
services.AddControllers();
}
}
// Controller
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
// 构造函数注入
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto dto)
{
_logger.LogInformation("Creating user: {Name}", dto.Name);
var user = await _userService.CreateUserAsync(dto);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var user = await _userService.GetUserByIdAsync(id);
return user != null ? Ok(user) : NotFound();
}
}
场景2:仓储模式 + 工作单元
// 工作单元接口
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
IOrderRepository Orders { get; }
Task<int> SaveChangesAsync();
}
// 工作单元实现
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context)
{
_context = context;
Users = new UserRepository(context);
Orders = new OrderRepository(context);
}
public IUserRepository Users { get; }
public IOrderRepository Orders { get; }
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context?.Dispose();
}
}
// 服务注册
services.AddScoped<IUnitOfWork, UnitOfWork>();
// 使用
public class OrderService
{
private readonly IUnitOfWork _unitOfWork;
public OrderService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task CreateOrderAsync(int userId, List<OrderItem> items)
{
var user = await _unitOfWork.Users.GetByIdAsync(userId);
if (user == null) throw new Exception("User not found");
var order = new Order { UserId = userId, Items = items };
await _unitOfWork.Orders.AddAsync(order);
// 一次性保存所有更改
await _unitOfWork.SaveChangesAsync();
}
}
场景3:配置选项模式
// 配置类
public class EmailSettings
{
public string SmtpServer { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
// appsettings.json
{
"EmailSettings": {
"SmtpServer": "smtp.example.com",
"Port": 587,
"Username": "user@example.com",
"Password": "password"
}
}
// 注册配置
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
// 使用配置
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions<EmailSettings> options)
{
_settings = options.Value;
}
public void SendEmail(string to, string subject, string body)
{
// 使用 _settings.SmtpServer, _settings.Port 等
}
}
场景4:命名服务
// 注册多个相同接口的不同实现
services.AddSingleton<INotificationService, EmailNotificationService>();
services.AddSingleton<INotificationService, SmsNotificationService>();
// 使用 Keyed Services (.NET 8+)
services.AddKeyedSingleton<INotificationService, EmailNotificationService>("email");
services.AddKeyedSingleton<INotificationService, SmsNotificationService>("sms");
public class NotificationManager
{
private readonly INotificationService _emailService;
private readonly INotificationService _smsService;
public NotificationManager(
[FromKeyedServices("email")] INotificationService emailService,
[FromKeyedServices("sms")] INotificationService smsService)
{
_emailService = emailService;
_smsService = smsService;
}
}
场景5:装饰器模式
// 基础服务
public interface IUserService
{
Task<User> GetUserAsync(int id);
}
public class UserService : IUserService
{
public async Task<User> GetUserAsync(int id)
{
// 从数据库获取
return await Task.FromResult(new User { Id = id });
}
}
// 缓存装饰器
public class CachedUserService : IUserService
{
private readonly IUserService _innerService;
private readonly ICacheService _cache;
public CachedUserService(IUserService innerService, ICacheService cache)
{
_innerService = innerService;
_cache = cache;
}
public async Task<User> GetUserAsync(int id)
{
var cacheKey = $"user_{id}";
if (_cache.TryGet(cacheKey, out User cachedUser))
{
return cachedUser;
}
var user = await _innerService.GetUserAsync(id);
_cache.Set(cacheKey, user, TimeSpan.FromMinutes(10));
return user;
}
}
// 注册
services.AddScoped<UserService>();
services.AddScoped<IUserService>(provider =>
{
var innerService = provider.GetRequiredService<UserService>();
var cache = provider.GetRequiredService<ICacheService>();
return new CachedUserService(innerService, cache);
});
高级功能
1. 服务定位器模式(不推荐)
// ❌ 服务定位器模式(反模式)
public class OrderService
{
private readonly IServiceProvider _serviceProvider;
public OrderService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void ProcessOrder()
{
// 在方法中解析依赖(隐藏依赖)
var repository = _serviceProvider.GetService<IOrderRepository>();
// ...
}
}
// ✅ 推荐:构造函数注入
public class OrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
}
2. 工厂模式 + DI
// 工厂接口
public interface INotificationFactory
{
INotificationService Create(NotificationType type);
}
// 工厂实现
public class NotificationFactory : INotificationFactory
{
private readonly IServiceProvider _serviceProvider;
public NotificationFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public INotificationService Create(NotificationType type)
{
return type switch
{
NotificationType.Email => _serviceProvider.GetRequiredService<EmailNotificationService>(),
NotificationType.Sms => _serviceProvider.GetRequiredService<SmsNotificationService>(),
NotificationType.Push => _serviceProvider.GetRequiredService<PushNotificationService>(),
_ => throw new ArgumentException("Unknown notification type")
};
}
}
// 注册
services.AddScoped<EmailNotificationService>();
services.AddScoped<SmsNotificationService>();
services.AddScoped<PushNotificationService>();
services.AddScoped<INotificationFactory, NotificationFactory>();
3. 泛型服务注册
// 泛型接口
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task AddAsync(T entity);
}
// 泛型实现
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
public async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}
public async Task AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
await _context.SaveChangesAsync();
}
}
// 注册开放泛型
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
// 使用
public class UserService
{
private readonly IRepository<User> _userRepository;
private readonly IRepository<Order> _orderRepository;
public UserService(
IRepository<User> userRepository,
IRepository<Order> orderRepository)
{
_userRepository = userRepository;
_orderRepository = orderRepository;
}
}
4. 条件注册
// 只在开发环境注册
if (environment.IsDevelopment())
{
services.AddScoped<IEmailService, MockEmailService>();
}
else
{
services.AddScoped<IEmailService, SmtpEmailService>();
}
// TryAdd 系列(仅在未注册时才注册)
services.TryAddScoped<IUserService, UserService>();
services.TryAddScoped<IUserService, AnotherUserService>(); // 不会注册(已存在)
// Replace(替换已注册的服务)
services.Replace(ServiceDescriptor.Scoped<IUserService, NewUserService>());
// RemoveAll(移除所有匹配的注册)
services.RemoveAll<IUserService>();
测试中的DI
单元测试
[TestClass]
public class UserServiceTests
{
[TestMethod]
public async Task CreateUser_ShouldSaveToRepository()
{
// Arrange
var mockRepository = new Mock<IUserRepository>();
var mockEmailService = new Mock<IEmailService>();
var service = new UserService(
mockRepository.Object,
mockEmailService.Object);
var user = new User { Name = "张三", Email = "test@example.com" };
// Act
await service.CreateUserAsync(user);
// Assert
mockRepository.Verify(r => r.AddAsync(user), Times.Once);
mockEmailService.Verify(e => e.SendWelcomeEmail(user.Email), Times.Once);
}
}
集成测试
public class IntegrationTests : IDisposable
{
private readonly ServiceProvider _serviceProvider;
public IntegrationTests()
{
var services = new ServiceCollection();
// 注册真实服务
services.AddDbContext<TestDbContext>(options =>
options.UseInMemoryDatabase("TestDb"));
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserService, UserService>();
_serviceProvider = services.BuildServiceProvider();
}
[Fact]
public async Task FullWorkflow_ShouldWork()
{
using var scope = _serviceProvider.CreateScope();
var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
// 测试完整流程
var user = await userService.CreateUserAsync(new User { Name = "张三" });
var retrieved = await userService.GetUserByIdAsync(user.Id);
Assert.NotNull(retrieved);
Assert.Equal("张三", retrieved.Name);
}
public void Dispose()
{
_serviceProvider?.Dispose();
}
}
最佳实践
1. 依赖接口而非实现
// ✅ 依赖接口
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
}
// ❌ 依赖具体类
public class UserService
{
private readonly SqlUserRepository _repository;
public UserService(SqlUserRepository repository)
{
_repository = repository;
}
}
2. 避免循环依赖
// ❌ 循环依赖
public class ServiceA
{
public ServiceA(ServiceB serviceB) { }
}
public class ServiceB
{
public ServiceB(ServiceA serviceA) { } // 循环依赖!
}
// ✅ 重构避免循环
public interface ISharedService { }
public class ServiceA
{
public ServiceA(ISharedService shared) { }
}
public class ServiceB
{
public ServiceB(ISharedService shared) { }
}
3. 选择合适的生命周期
// ✅ 正确的生命周期选择
services.AddSingleton<IConfiguration>(configuration); // 配置:单例
services.AddSingleton<ICacheService, MemoryCacheService>(); // 缓存:单例
services.AddScoped<DbContext>(); // 数据库上下文:作用域
services.AddScoped<IUserRepository, UserRepository>(); // 仓储:作用域
services.AddTransient<IEmailService, EmailService>(); // 邮件服务:瞬时
4. 构造函数参数数量
// ❌ 参数过多(违反单一职责)
public class OrderService
{
public OrderService(
IOrderRepository orderRepo,
IProductRepository productRepo,
IUserRepository userRepo,
IPaymentService payment,
IEmailService email,
ILogService log,
ICacheService cache)
{
// 太多依赖说明类职责过多
}
}
// ✅ 重构为更小的服务
public class OrderService
{
public OrderService(
IOrderRepository repository,
IOrderProcessor processor)
{
// 职责更清晰
}
}
5. 验证必需的依赖
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
}
常见错误
错误1:Captive Dependency
// ❌ 单例服务依赖作用域服务
services.AddSingleton<SingletonService>(); // 单例
services.AddScoped<ScopedService>(); // 作用域
public class SingletonService
{
// 危险:单例持有作用域服务的引用
public SingletonService(ScopedService scoped)
{
// scoped 实例会被单例"捕获",生命周期延长
}
}
// ✅ 正确做法
public class SingletonService
{
private readonly IServiceProvider _provider;
public SingletonService(IServiceProvider provider)
{
_provider = provider;
}
public void DoWork()
{
using var scope = _provider.CreateScope();
var scoped = scope.ServiceProvider.GetRequiredService<ScopedService>();
// 使用 scoped
}
}
错误2:忘记释放资源
// ❌ 手动创建 ServiceProvider 忘记释放
var provider = services.BuildServiceProvider();
var service = provider.GetService<MyService>();
// 忘记调用 provider.Dispose()
// ✅ 使用 using
using (var provider = services.BuildServiceProvider())
{
var service = provider.GetService<MyService>();
// 自动释放
}
总结
DI的核心优势
- 松耦合:降低组件间依赖
- 可测试性:易于单元测试
- 可维护性:易于替换实现
- 灵活性:运行时配置依赖
生命周期选择指南
- Singleton:无状态、线程安全、全局共享
- Scoped:请求作用域、数据库上下文
- Transient:轻量级、每次使用都需要新实例
设计原则
- 依赖倒置原则:依赖抽象而非具体
- 单一职责原则:一个类只做一件事
- 接口隔离原则:接口应该小而专注
- 开闭原则:对扩展开放,对修改关闭
提示:依赖注入是现代软件架构的基石,掌握DI是成为优秀.NET开发者的必经之路。
更多推荐



所有评论(0)