.NET 依赖注入深入详解

原为链接:https://www.cnblogs.com/ysmc/p/18796964

.NET 依赖注入深入详解

依赖注入(Dependency Injection, DI)是.NET Core .NET 5/6/7/8/9/10+中最重要的设计模式之一,下面我将从多个维度详细解释它的工作原理和使用方法。

一、核心概念解析

1. 什么是依赖?

当一个类A需要类B才能正常工作时,我们就说类A”依赖”于类B。例如:

public class OrderService
{
    private readonly ILogger _logger; // OrderService依赖于ILogger
    
    public OrderService(ILogger logger)
    {
        _logger = logger;
    }
}

2. 传统方式的问题

没有DI时,我们可能会这样写:

public class OrderService
{
    private readonly FileLogger _logger = new FileLogger(); // 直接创建具体实现
    
    // ...
}

这种方式的缺点:

  • 紧耦合:OrderService直接依赖FileLogger

  • 难以测试:无法轻松替换为测试用的Logger

  • 难以修改:如果要改用DatabaseLogger,需要修改OrderService代码

二、.NET DI 容器详解

1. 服务注册方式

在Program.cs/Startup.cs中有三种主要注册方式:

// 1. 注册具体类型
services.AddTransient<EmailService>();

// 2. 注册接口-实现映射
services.AddScoped<IEmailService, SmtpEmailService>();

// 3. 注册现有实例
var logger = new FileLogger();
services.AddSingleton<ILogger>(logger);

2. 生命周期详解

生命周期 描述 适用场景 示例
Transient 每次请求都创建新实例 轻量级、无状态服务 工具类、DTO映射
Scoped 同一请求内共享实例 需要请求上下文的服务 DbContext、用户会话
Singleton 整个应用生命周期一个实例 全局共享资源 配置、缓存、日志

 

 

 

3. 高级注册技巧

// 注册多个实现
services.AddTransient<IMessageService, SmsService>();
services.AddTransient<IMessageService, EmailService>();

// 命名注册
services.AddTransient<IMessageService>("SMS", typeof(SmsService));

// 泛型注册
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// 委托工厂
services.AddTransient<IService>(sp => 
    new Service(sp.GetRequiredService<IOtherService>()));

三、注入方式大全

1. 构造函数注入(最推荐)

public class ProductController
{
    private readonly IProductService _service;
    
    public ProductController(IProductService service)
    {
        _service = service; // 由DI容器自动注入
    }
}

2. 方法注入

public class ReportGenerator
{
    public void Generate(IReportFormatter formatter) 
    {
        // 使用方法参数注入
    }
}

3. 属性注入(不推荐但某些场景需要,如blazor)

public class NotificationService
{
    [Inject] // 需要特定属性标记
    public ILogger Logger { get; set; }
}

4. 从容器直接解析(应避免,但有时必要)

var service = serviceProvider.GetRequiredService<IMyService>();

四、实际应用场景

1. 分层架构中的DI

// 基础设施层
services.AddDbContext<AppDbContext>(options => 
    options.UseSqlServer(Configuration.GetConnectionString("Default")));

// 应用层
services.AddScoped<IOrderService, OrderService>();

// 领域层
services.AddTransient<IDomainEventDispatcher, DomainEventDispatcher>();

// 表现层
services.AddControllersWithViews();

2. 选项模式(Options Pattern)

// 注册配置
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

// 注入使用
public class EmailService
{
    private readonly EmailSettings _settings;
    
    public EmailService(IOptions<EmailSettings> options)
    {
        _settings = options.Value;
    }
}

3. 日志集成

public class ProductService
{
    private readonly ILogger<ProductService> _logger;
    
    public ProductService(ILogger<ProductService> logger)
    {
        _logger = logger; // 自动注入日志系统
    }
}

五、高级主题

1. 第三方容器集成

// Autofac示例
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureContainer<ContainerBuilder>(builder =>
    {
        builder.RegisterModule(new MyAutofacModule());
    });

2. 装饰器模式

// 原始服务
services.AddScoped<IDataService, DataService>();

// 装饰器
services.Decorate<IDataService, CachingDataService>();

3. 验证服务注册

// 检查所有服务是否已注册
var provider = services.BuildServiceProvider();
foreach (var service in services)
{
    provider.GetRequiredService(service.ServiceType); // 会抛出异常如果未注册
}

六、最佳实践

  1. 构造函数保持简单:只注入必要的依赖

  2. 避免服务定位器模式:不要滥用GetService

  3. 注意生命周期:避免Scoped服务被Singleton服务引用

  4. 使用接口:尽量依赖抽象而非具体实现

  5. 避免过度注入:如一个类注入超过5个服务,考虑重构

七、常见问题解决

循环依赖问题

// 错误示例
class A { public A(B b) {} }
class B { public B(A a) {} }

// 解决方案:
// 1. 重构设计,提取公共逻辑到第三个类
// 2. 使用属性注入或方法注入替代构造函数注入

多实现选择

// 注册多个实现
services.AddTransient<IMessageService, EmailService>();
services.AddKeyedTransient<IMessageService, SmsService>("SMS");

// 解析所有实现
var services = provider.GetServices<IMessageService>();

// 命名解析
var smsService = provider.GetRequiredKeyedService<IMessageService>("SMS");

感谢各位大佬的观看!

来源链接:https://www.cnblogs.com/ysmc/p/18796964

© 版权声明
THE END
支持一下吧
点赞14 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容