跳转到内容

依赖注入

MiCake 扩展了 .NET Core 的依赖注入系统,提供了更便捷的服务注册方式,包括自动注册、特性标记等功能。

MiCake 完全兼容 .NET Core 的标准依赖注入:

public class MyModule : MiCakeModule
{
public override void ConfigureServices(ModuleConfigServiceContext context)
{
var services = context.Services;
// 瞬时(Transient)- 每次请求都创建新实例
services.AddTransient<ITransientService, TransientService>();
// 作用域(Scoped)- 每个请求作用域内是同一实例
services.AddScoped<IScopedService, ScopedService>();
// 单例(Singleton)- 整个应用程序生命周期内是同一实例
services.AddSingleton<ISingletonService, SingletonService>();
base.ConfigureServices(context);
}
}

MiCake 提供了多种自动注册服务的方式。

实现以下接口的服务会自动注册到 DI 容器:

using MiCake.Core.DependencyInjection;
// 瞬时服务
public class EmailService : ITransientService
{
public void SendEmail(string to, string subject, string body)
{
// 发送邮件逻辑
}
}
// 作用域服务
public class OrderService : IScopedService
{
private readonly IRepository<Order, int> _orderRepository;
public OrderService(IRepository<Order, int> orderRepository)
{
_orderRepository = orderRepository;
}
public async Task CreateOrder(CreateOrderDto dto)
{
// 创建订单逻辑
}
}
// 单例服务
public class CacheService : ISingletonService
{
private readonly Dictionary<string, object> _cache = new();
public void Set(string key, object value)
{
_cache[key] = value;
}
public T Get<T>(string key)
{
return _cache.TryGetValue(key, out var value) ? (T)value : default;
}
}

无需手动注册,MiCake 会自动扫描并注册这些服务。

使用 [InjectService] 特性提供更精细的控制:

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 会自动将服务注册为以下类型:

public class OrderService : IOrderService, IScopedService
{
// 自动注册为:
// - OrderService(实现类)
// - IOrderService(接口)
}
// 使用
public class OrderController
{
private readonly IOrderService _orderService; // ✅ 可以注入接口
private readonly OrderService _concreteService; // ✅ 也可以注入实现类
public OrderController(IOrderService orderService)
{
_orderService = orderService;
}
}

每次请求都创建新实例:

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(不同)
}
}

适用场景:

  • 轻量级的无状态服务
  • 不需要共享状态的服务

在一个请求作用域内是同一实例:

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)

整个应用程序生命周期内是同一实例:

public class ConfigurationService : ISingletonService
{
public string AppVersion { get; } = "1.0.0";
public DateTime StartTime { get; } = DateTime.UtcNow;
}
// 所有注入都是同一实例
// service1.StartTime == service2.StartTime == service3.StartTime

适用场景:

  • 配置服务
  • 缓存服务
  • 日志服务
  • 无状态的工具类

MiCake 会自动暴露以下类型:

public class OrderService : IOrderService, IService, IScopedService
{
// 自动暴露:
// 1. 实现类本身: OrderService
// 2. 所有公共接口(除了标记接口): IOrderService, IService
// 不会暴露: IScopedService(这是标记接口)
}

使用 [InjectService] 特性自定义暴露的服务类型:

// 只暴露接口
[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
}

优先注入接口而不是实现类:

// ✅ 正确:注入接口
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;
}
}

不要注入 IServiceProvider 来获取服务:

// ❌ 错误:服务定位器模式
public class OrderService
{
private readonly IServiceProvider _serviceProvider;
public OrderService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void ProcessOrder()
{
var productService = _serviceProvider.GetService<IProductService>();
// ...
}
}
// ✅ 正确:构造函数注入
public class OrderService
{
private readonly IProductService _productService;
public OrderService(IProductService productService)
{
_productService = productService;
}
public void ProcessOrder()
{
// 直接使用注入的服务
}
}
// ✅ 正确:DbContext 使用 Scoped
public class MyDbContext : DbContext, IScopedService
{
// 每个请求一个实例
}
// ✅ 正确:缓存服务使用 Singleton
public class MemoryCacheService : ISingletonService
{
// 全局单一实例
}
// ❌ 错误:DbContext 使用 Singleton
public class MyDbContext : DbContext, ISingletonService
{
// 会导致线程安全问题
}
// ❌ 错误:循环依赖
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) { }
}
// ❌ 错误:单例服务不线程安全
public class CacheService : ISingletonService
{
private Dictionary<string, object> _cache = new();
public void Set(string key, object value)
{
_cache[key] = value; // 线程不安全
}
}
// ✅ 正确:使用线程安全的集合
public class CacheService : ISingletonService
{
private ConcurrentDictionary<string, object> _cache = new();
public void Set(string key, object value)
{
_cache[key] = value; // 线程安全
}
}

在某些情况下,您可能需要手动注册服务:

public class MyModule : MiCakeModule
{
public override void ConfigureServices(ModuleConfigServiceContext context)
{
var services = context.Services;
// 注册实现
services.AddScoped<IOrderService, OrderService>();
// 注册工厂方法
services.AddScoped<IEmailService>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
var smtpServer = config["Email:SmtpServer"];
return new EmailService(smtpServer);
});
// 注册已存在的实例
var cacheService = new CacheService();
services.AddSingleton<ICacheService>(cacheService);
// 尝试注册(如果已存在则跳过)
services.TryAddScoped<IProductService, ProductService>();
// 替换已有注册
services.Replace(ServiceDescriptor.Scoped<IOrderService, NewOrderService>());
base.ConfigureServices(context);
}
}

通过构造函数注入:

[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly IOrderService _orderService;
private readonly ILogger<OrderController> _logger;
public OrderController(
IOrderService orderService,
ILogger<OrderController> logger)
{
_orderService = orderService;
_logger = logger;
}
}

ServiceProvider 获取:

public class MyModule : MiCakeModule
{
public override void Initialization(ModuleInitializationContext context)
{
var serviceProvider = context.ServiceProvider;
// 获取必需的服务(不存在会抛出异常)
var dbContext = serviceProvider.GetRequiredService<MyDbContext>();
// 获取可选的服务(不存在返回 null)
var cacheService = serviceProvider.GetService<ICacheService>();
base.Initialization(context);
}
}

当您需要在非 DI 管理的类中获取服务时:

public class MyHelper
{
public static void DoSomething(IServiceProvider serviceProvider)
{
var orderService = serviceProvider.GetRequiredService<IOrderService>();
// 使用服务
}
}
services.TryAddScoped<IOrderService, OrderService>();
services.TryAddScoped<IOrderService, NewOrderService>(); // 不会生效,因为已注册
[InjectService(ServiceLifetime.Scoped, TryRegister = true)]
public class OrderService : IOrderService
{
// 仅在 IOrderService 未注册时才注册
}
  1. 生命周期选择:根据服务的特性选择合适的生命周期
  2. 避免内存泄漏:不要在长生命周期服务中持有短生命周期服务的引用
  3. 线程安全:单例服务必须是线程安全的
  4. 避免循环依赖:设计时注意服务间的依赖关系
  5. 接口优先:优先通过接口注入服务