时间:2025-03-27 22:01
人气:
作者:admin
原为链接:https://www.cnblogs.com/ysmc/p/18796964
依赖注入(Dependency Injection, DI)是.NET Core .NET 5/6/7/8/9/10+中最重要的设计模式之一,下面我将从多个维度详细解释它的工作原理和使用方法。
当一个类A需要类B才能正常工作时,我们就说类A"依赖"于类B。例如:
public class OrderService { private readonly ILogger _logger; // OrderService依赖于ILogger public OrderService(ILogger logger) { _logger = logger; } }
没有DI时,我们可能会这样写:
public class OrderService { private readonly FileLogger _logger = new FileLogger(); // 直接创建具体实现 // ... }
这种方式的缺点:
紧耦合:OrderService直接依赖FileLogger
难以测试:无法轻松替换为测试用的Logger
难以修改:如果要改用DatabaseLogger,需要修改OrderService代码
在Program.cs/Startup.cs中有三种主要注册方式:
// 1. 注册具体类型 services.AddTransient<EmailService>(); // 2. 注册接口-实现映射 services.AddScoped<IEmailService, SmtpEmailService>(); // 3. 注册现有实例 var logger = new FileLogger(); services.AddSingleton<ILogger>(logger);
| 生命周期 | 描述 | 适用场景 | 示例 |
|---|---|---|---|
| Transient | 每次请求都创建新实例 | 轻量级、无状态服务 | 工具类、DTO映射 |
| Scoped | 同一请求内共享实例 | 需要请求上下文的服务 | DbContext、用户会话 |
| Singleton | 整个应用生命周期一个实例 | 全局共享资源 | 配置、缓存、日志 |
// 注册多个实现 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>()));
public class ProductController { private readonly IProductService _service; public ProductController(IProductService service) { _service = service; // 由DI容器自动注入 } }
public class ReportGenerator { public void Generate(IReportFormatter formatter) { // 使用方法参数注入 } }
public class NotificationService { [Inject] // 需要特定属性标记 public ILogger Logger { get; set; } }
var service = serviceProvider.GetRequiredService<IMyService>();
// 基础设施层 services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"))); // 应用层 services.AddScoped<IOrderService, OrderService>(); // 领域层 services.AddTransient<IDomainEventDispatcher, DomainEventDispatcher>(); // 表现层 services.AddControllersWithViews();
// 注册配置 services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings")); // 注入使用 public class EmailService { private readonly EmailSettings _settings; public EmailService(IOptions<EmailSettings> options) { _settings = options.Value; } }
public class ProductService { private readonly ILogger<ProductService> _logger; public ProductService(ILogger<ProductService> logger) { _logger = logger; // 自动注入日志系统 } }
// Autofac示例 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureContainer<ContainerBuilder>(builder => { builder.RegisterModule(new MyAutofacModule()); });
// 原始服务 services.AddScoped<IDataService, DataService>(); // 装饰器 services.Decorate<IDataService, CachingDataService>();
// 检查所有服务是否已注册 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<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
Microsoft Agent Framework Skills 执行 Scripts(实
EF Core 原生 SQL 实战:FromSql、SqlQuery 与对