--- title: 依赖注入 description: 了解 MiCake 的依赖注入增强功能,包括自动注册、服务标记接口和生命周期管理 --- MiCake 扩展了 .NET Core 的依赖注入系统,提供了更便捷的服务注册方式,包括自动注册、特性标记等功能。 ## 标准依赖注入 MiCake 完全兼容 .NET Core 的标准依赖注入: ```csharp public class MyModule : MiCakeModule { public override void ConfigureServices(ModuleConfigServiceContext context) { var services = context.Services; // 瞬时(Transient)- 每次请求都创建新实例 services.AddTransient(); // 作用域(Scoped)- 每个请求作用域内是同一实例 services.AddScoped(); // 单例(Singleton)- 整个应用程序生命周期内是同一实例 services.AddSingleton(); base.ConfigureServices(context); } } ``` ## 自动注册 MiCake 提供了多种自动注册服务的方式。 ### 方式 1:实现标记接口 实现以下接口的服务会自动注册到 DI 容器: ```csharp using MiCake.Core.DependencyInjection; // 瞬时服务 public class EmailService : ITransientService { public void SendEmail(string to, string subject, string body) { // 发送邮件逻辑 } } // 作用域服务 public class OrderService : IScopedService { private readonly IRepository _orderRepository; public OrderService(IRepository orderRepository) { _orderRepository = orderRepository; } public async Task CreateOrder(CreateOrderDto dto) { // 创建订单逻辑 } } // 单例服务 public class CacheService : ISingletonService { private readonly Dictionary _cache = new(); public void Set(string key, object value) { _cache[key] = value; } public T Get(string key) { return _cache.TryGetValue(key, out var value) ? (T)value : default; } } ``` **无需手动注册,MiCake 会自动扫描并注册这些服务。** ### 方式 2:使用 InjectService 特性 使用 `[InjectService]` 特性提供更精细的控制: ```csharp using MiCake.Core.DependencyInjection; using Microsoft.Extensions.DependencyInjection; // 基本用法 [InjectService(ServiceLifetime.Scoped)] public class ProductService : IProductService { // ... } // 指定暴露的服务类型 [InjectService(ServiceLifetime.Scoped, ExposeServices = new[] { typeof(IProductService), typeof(IService) })] public class ProductService : IProductService, IService { // 会注册为 IProductService 和 IService } // 尝试注册(如果已存在则跳过) [InjectService(ServiceLifetime.Scoped, TryRegister = true)] public class ProductService : IProductService { // 如果 IProductService 已注册,则跳过 } // 替换已有注册 [InjectService(ServiceLifetime.Scoped, ReplaceServices = true)] public class NewProductService : IProductService { // 替换已注册的 IProductService } ``` ### 自动注册的服务类型 MiCake 会自动将服务注册为以下类型: ```csharp public class OrderService : IOrderService, IScopedService { // 自动注册为: // - OrderService(实现类) // - IOrderService(接口) } // 使用 public class OrderController { private readonly IOrderService _orderService; // ✅ 可以注入接口 private readonly OrderService _concreteService; // ✅ 也可以注入实现类 public OrderController(IOrderService orderService) { _orderService = orderService; } } ``` ## 生命周期 ### Transient(瞬时) 每次请求都创建新实例: ```csharp public class GuidService : ITransientService { public Guid Id { get; } = Guid.NewGuid(); } // 每次注入都是不同的实例 public class MyController { public MyController(GuidService service1, GuidService service2) { Console.WriteLine(service1.Id); // 例如:123e4567-e89b-12d3-a456-426614174000 Console.WriteLine(service2.Id); // 例如:9876-5432-10dc-ba98-765432109876(不同) } } ``` **适用场景:** - 轻量级的无状态服务 - 不需要共享状态的服务 ### Scoped(作用域) 在一个请求作用域内是同一实例: ```csharp public class RequestContextService : IScopedService { public Guid RequestId { get; } = Guid.NewGuid(); } // 同一请求中的所有注入都是同一实例 public class MyController { public MyController(RequestContextService service1, RequestContextService service2) { Console.WriteLine(service1.RequestId); // 例如:123e4567-e89b-12d3-a456-426614174000 Console.WriteLine(service2.RequestId); // 123e4567-e89b-12d3-a456-426614174000(相同) } } ``` **适用场景:** - 需要在请求期间共享状态的服务 - 数据库上下文(DbContext) - 工作单元(Unit of Work) ### Singleton(单例) 整个应用程序生命周期内是同一实例: ```csharp public class ConfigurationService : ISingletonService { public string AppVersion { get; } = "1.0.0"; public DateTime StartTime { get; } = DateTime.UtcNow; } // 所有注入都是同一实例 // service1.StartTime == service2.StartTime == service3.StartTime ``` **适用场景:** - 配置服务 - 缓存服务 - 日志服务 - 无状态的工具类 ## 服务暴露 ### 默认暴露规则 MiCake 会自动暴露以下类型: ```csharp public class OrderService : IOrderService, IService, IScopedService { // 自动暴露: // 1. 实现类本身: OrderService // 2. 所有公共接口(除了标记接口): IOrderService, IService // 不会暴露: IScopedService(这是标记接口) } ``` ### 自定义暴露 使用 `[InjectService]` 特性自定义暴露的服务类型: ```csharp // 只暴露接口 [InjectService(ServiceLifetime.Scoped, ExposeServices = new[] { typeof(IOrderService) })] public class OrderService : IOrderService, IService { // 只注册为 IOrderService } // 只暴露实现类 [InjectService(ServiceLifetime.Scoped, ExposeServices = new[] { typeof(OrderService) })] public class OrderService : IOrderService { // 只注册为 OrderService } // 暴露多个类型 [InjectService(ServiceLifetime.Scoped, ExposeServices = new[] { typeof(IOrderService), typeof(IService), typeof(OrderService) })] public class OrderService : IOrderService, IService { // 注册为 IOrderService, IService 和 OrderService } ``` ## 依赖注入最佳实践 ### 1. 接口优于实现 优先注入接口而不是实现类: ```csharp // ✅ 正确:注入接口 public class OrderController { private readonly IOrderService _orderService; public OrderController(IOrderService orderService) { _orderService = orderService; } } // ❌ 不推荐:注入实现类 public class OrderController { private readonly OrderService _orderService; public OrderController(OrderService orderService) { _orderService = orderService; } } ``` ### 2. 避免服务定位器模式 不要注入 `IServiceProvider` 来获取服务: ```csharp // ❌ 错误:服务定位器模式 public class OrderService { private readonly IServiceProvider _serviceProvider; public OrderService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void ProcessOrder() { var productService = _serviceProvider.GetService(); // ... } } // ✅ 正确:构造函数注入 public class OrderService { private readonly IProductService _productService; public OrderService(IProductService productService) { _productService = productService; } public void ProcessOrder() { // 直接使用注入的服务 } } ``` ### 3. 选择正确的生命周期 ```csharp // ✅ 正确:DbContext 使用 Scoped public class MyDbContext : DbContext, IScopedService { // 每个请求一个实例 } // ✅ 正确:缓存服务使用 Singleton public class MemoryCacheService : ISingletonService { // 全局单一实例 } // ❌ 错误:DbContext 使用 Singleton public class MyDbContext : DbContext, ISingletonService { // 会导致线程安全问题 } ``` ### 4. 避免循环依赖 ```csharp // ❌ 错误:循环依赖 public class ServiceA : IScopedService { public ServiceA(ServiceB serviceB) { } } public class ServiceB : IScopedService { public ServiceB(ServiceA serviceA) { } // 循环依赖! } // ✅ 正确:提取接口或使用中介者 public interface IServiceAProvider { void DoSomething(); } public class ServiceA : IServiceAProvider, IScopedService { public void DoSomething() { } } public class ServiceB : IScopedService { public ServiceB(IServiceAProvider serviceAProvider) { } } ``` ### 5. 单例服务的线程安全 ```csharp // ❌ 错误:单例服务不线程安全 public class CacheService : ISingletonService { private Dictionary _cache = new(); public void Set(string key, object value) { _cache[key] = value; // 线程不安全 } } // ✅ 正确:使用线程安全的集合 public class CacheService : ISingletonService { private ConcurrentDictionary _cache = new(); public void Set(string key, object value) { _cache[key] = value; // 线程安全 } } ``` ## 手动注册服务 在某些情况下,您可能需要手动注册服务: ```csharp public class MyModule : MiCakeModule { public override void ConfigureServices(ModuleConfigServiceContext context) { var services = context.Services; // 注册实现 services.AddScoped(); // 注册工厂方法 services.AddScoped(sp => { var config = sp.GetRequiredService(); var smtpServer = config["Email:SmtpServer"]; return new EmailService(smtpServer); }); // 注册已存在的实例 var cacheService = new CacheService(); services.AddSingleton(cacheService); // 尝试注册(如果已存在则跳过) services.TryAddScoped(); // 替换已有注册 services.Replace(ServiceDescriptor.Scoped()); base.ConfigureServices(context); } } ``` ## 获取服务 ### 在控制器中 通过构造函数注入: ```csharp [ApiController] [Route("api/[controller]")] public class OrderController : ControllerBase { private readonly IOrderService _orderService; private readonly ILogger _logger; public OrderController( IOrderService orderService, ILogger logger) { _orderService = orderService; _logger = logger; } } ``` ### 在模块初始化中 从 `ServiceProvider` 获取: ```csharp public class MyModule : MiCakeModule { public override void Initialization(ModuleInitializationContext context) { var serviceProvider = context.ServiceProvider; // 获取必需的服务(不存在会抛出异常) var dbContext = serviceProvider.GetRequiredService(); // 获取可选的服务(不存在返回 null) var cacheService = serviceProvider.GetService(); base.Initialization(context); } } ``` ### 在非 DI 管理的类中 当您需要在非 DI 管理的类中获取服务时: ```csharp public class MyHelper { public static void DoSomething(IServiceProvider serviceProvider) { var orderService = serviceProvider.GetRequiredService(); // 使用服务 } } ``` ## 条件注册 ### TryAdd - 仅在未注册时注册 ```csharp services.TryAddScoped(); services.TryAddScoped(); // 不会生效,因为已注册 ``` ### 使用特性 ```csharp [InjectService(ServiceLifetime.Scoped, TryRegister = true)] public class OrderService : IOrderService { // 仅在 IOrderService 未注册时才注册 } ``` ## 注意事项 1. **生命周期选择**:根据服务的特性选择合适的生命周期 2. **避免内存泄漏**:不要在长生命周期服务中持有短生命周期服务的引用 3. **线程安全**:单例服务必须是线程安全的 4. **避免循环依赖**:设计时注意服务间的依赖关系 5. **接口优先**:优先通过接口注入服务