原为链接: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
// 2. 注册接口-实现映射
services.AddScoped
// 3. 注册现有实例
var logger = new FileLogger();
services.AddSingleton
2. 生命周期详解
生命周期描述适用场景示例
Transient
每次请求都创建新实例
轻量级、无状态服务
工具类、DTO映射
Scoped
同一请求内共享实例
需要请求上下文的服务
DbContext、用户会话
Singleton
整个应用生命周期一个实例
全局共享资源
配置、缓存、日志
3. 高级注册技巧
// 注册多个实现
services.AddTransient
services.AddTransient
// 命名注册
services.AddTransient
// 泛型注册
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
// 委托工厂
services.AddTransient
new Service(sp.GetRequiredService
三、注入方式大全
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
四、实际应用场景
1. 分层架构中的DI
// 基础设施层
services.AddDbContext
options.UseSqlServer(Configuration.GetConnectionString("Default")));
// 应用层
services.AddScoped
// 领域层
services.AddTransient
// 表现层
services.AddControllersWithViews();
2. 选项模式(Options Pattern)
// 注册配置
services.Configure
// 注入使用
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions
{
_settings = options.Value;
}
}
3. 日志集成
public class ProductService
{
private readonly ILogger
public ProductService(ILogger
{
_logger = logger; // 自动注入日志系统
}
}
五、高级主题
1. 第三方容器集成
// Autofac示例
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer
{
builder.RegisterModule(new MyAutofacModule());
});
2. 装饰器模式
// 原始服务
services.AddScoped
// 装饰器
services.Decorate
3. 验证服务注册
// 检查所有服务是否已注册
var provider = services.BuildServiceProvider();
foreach (var service in services)
{
provider.GetRequiredService(service.ServiceType); // 会抛出异常如果未注册
}
六、最佳实践
构造函数保持简单:只注入必要的依赖
避免服务定位器模式:不要滥用GetService
注意生命周期:避免Scoped服务被Singleton服务引用
使用接口:尽量依赖抽象而非具体实现
避免过度注入:如一个类注入超过5个服务,考虑重构
七、常见问题解决
循环依赖问题
// 错误示例
class A { public A(B b) {} }
class B { public B(A a) {} }
// 解决方案:
// 1. 重构设计,提取公共逻辑到第三个类
// 2. 使用属性注入或方法注入替代构造函数注入
多实现选择
// 注册多个实现
services.AddTransient
services.AddKeyedTransient
// 解析所有实现
var services = provider.GetServices
// 命名解析
var smsService = provider.GetRequiredKeyedService
感谢各位大佬的观看!