什么是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开发者的必经之路。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐