Skip to content

依赖注入

HMX平台基于ASP.NET Core的依赖注入(DI)系统,扩展了自动注册和模块化服务能力。本文档介绍项目中的依赖注入设计和使用方式。

依赖注入架构

整体流程

Program.Main()

  ├─ builder.InitializeModules<T>()     // 初始化模块系统
  │    │
  │    ├─ 注册框架基础服务
  │    ├─ 创建引擎(HmxEngine)
  │    ├─ 模块 PreInitialize            // 各模块注册服务
  │    └─ AutoRegister                   // 自动注册标记接口服务

  ├─ builder.Services.AddXxx()          // 手动注册其他服务

  └─ app.PostInitHiMindApplication()    // 完成模块初始化

       ├─ 模块 Initialize
       └─ 模块 PostInitialize

注册方式

项目支持三种服务注册方式:

方式说明适用场景
模块注册在模块 PreInitialize 中手动注册模块特定的服务
标记接口实现 ISingletonDependency 等接口通用服务,自动发现
手动注册Program.cs 中直接注册框架级别服务

标记接口自动注册

接口定义

接口命名空间生命周期说明
ISingletonDependencyHmx.Core.DependencyInjectionSingleton单例,全局唯一
IScopedDependencyHmx.Core.DependencyInjectionScoped作用域,每个请求一个实例
ITransientDependencyHmx.Core.DependencyInjectionTransient瞬时,每次创建新实例

使用示例

csharp
using Hmx.Core.DependencyInjection;

namespace YourProject.Services
{
    // 接口定义标记接口
    public interface ICacheService : ISingletonDependency
    {
        T Get<T>(string key);
        void Set<T>(string key, T value);
    }

    // 实现类自动注册
    public class RedisCacheService : ICacheService
    {
        public T Get<T>(string key) { /* ... */ }
        public void Set<T>(string key, T value) { /* ... */ }
    }

    // 作用域服务示例
    public interface IOrderRepository : IScopedDependency
    {
        Task<Order> GetByIdAsync(string id);
    }

    // 瞬时服务示例
    public interface IEmailSender : ITransientDependency
    {
        Task SendAsync(string to, string message);
    }
}

自动注册原理

csharp
// 框架内部实现(DependencyInjectionExtensions.AutoRegister)
public static void AutoRegister(this IServiceCollection services)
{
    var typeFinder = services.GetSingletonInstanceOrNull<ITypeFinder>();
    
    // 扫描所有实现 ISingletonDependency 的类
    var singletonTypes = typeFinder.FindClassesOfType<ISingletonDependency>();
    foreach (var type in singletonTypes)
    {
        // 注册该类实现的所有接口(排除标记接口本身)
        foreach (var interfaceType in type.GetInterfaces())
        {
            if (interfaceType != typeof(ISingletonDependency))
            {
                services.TryAddSingleton(interfaceType, type);
            }
        }
    }
    
    // 同理处理 IScopedDependency 和 ITransientDependency
}

注意:使用 TryAdd 方式注册,如果接口已有注册则不会覆盖。

模块内手动注册

在模块的 PreInitialize 方法中可以手动注册服务:

csharp
public class YourModule : HiMindModule
{
    public override Task PreInitialize(ApplicationPreInitializeContext context)
    {
        var services = context.Services;

        // 注册具体实现
        services.AddScoped<IOrderService, OrderService>();
        
        // 注册工厂模式
        services.AddSingleton<IDbConnection>(sp =>
        {
            var config = sp.GetRequiredService<IConfiguration>();
            var connStr = config.GetConnectionString("Default");
            return new SqlConnection(connStr);
        });
        
        // 注册配置绑定
        services.Configure<YourOptions>(
            context.Configuration.GetSection("YourOptions"));

        return base.PreInitialize(context);
    }
}

注册顺序

  1. 框架基础服务(缓存、对象映射、HttpContext等)
  2. Redis/消息队列等中间件服务
  3. 各模块 PreInitialize 注册
  4. AutoRegister 自动注册
  5. Program.cs 中手动注册

服务解析方式

AppService 与普通服务的区别

系统中有两种类型的服务,它们的依赖注入方式不同:

类型特征注入方式说明
AppService实现 IHmxAppService 接口EngineContext.Current.Resolve被动态WebAPI机制当作Controller
普通服务不实现 IHmxAppService构造函数注入正常的DI服务

为什么 AppService 不能用构造函数注入?

IHmxAppService 的实现类会被 HmxDynamicWebApiControllerFeatureProvider 识别为ASP.NET Core的Controller:

csharp
// 框架内部:将 IHmxAppService 实现类标记为Controller
protected override bool IsController(TypeInfo typeInfo)
{
    if (typeof(IHmxAppService).IsAssignableFrom(typeInfo)
        && typeInfo is { IsClass: true, IsAbstract: false, IsPublic: true })
    {
        return true;  // 视为Controller
    }
    return base.IsController(typeInfo);
}

由于基类 HmxAppServiceBase<T> 使用 EngineContext.Current.Resolve<T>() 获取依赖(Logger、Cache等),而不是通过构造函数参数,因此AppService类不适合使用构造函数注入

AppService 服务解析

csharp
// ✅ AppService 的正确写法:使用 EngineContext.Current.Resolve
public class AdminAppService : HmxAppServiceBase<AdminAppService>, IAdminAppService
{
    // 直接使用基类提供的属性(内部通过 EngineContext 解析)
    protected virtual ILogger<T> Logger { get; }
    protected virtual IHmxDistributedCache DistributedCache { get; }
    protected virtual IHmxCache MemoryCache { get; }
    
    public async Task<List<HmxUser>> GetUsersAsync(string keywords)
    {
        // 使用 Logger、Cache 等
        Logger.LogInformation("查询用户列表");
        
        // 获取数据库连接
        var db = GetDbContext();
        return await db.GetTable<HmxUser>().ToListAsync();
    }
}
csharp
// ❌ 错误:AppService 不要使用构造函数注入
public class AdminAppService : HmxAppServiceBase<AdminAppService>, IAdminAppService
{
    private readonly IMyService _myService;
    
    public AdminAppService(IMyService myService)  // ❌ 不推荐
    {
        _myService = myService;
    }
}

普通服务解析

csharp
// ✅ 普通服务:使用构造函数注入(推荐)
public class AiChatService
{
    private readonly ILogger<AiChatService> _log;
    private readonly MesDatabaseService _db;

    public AiChatService(
        ILogger<AiChatService> log,
        MesDatabaseService db)  // ✅ 构造函数注入
    {
        _log = log;
        _db = db;
    }
}

通过 EngineContext 解析服务

csharp
// 在任何地方解析服务(适用于静态方法、过滤器等场景)
var cache = EngineContext.Current.Resolve<IHmxCache>();
var logger = EngineContext.Current.Resolve<ILogger<MyClass>>();

框架内置服务

以下是框架自动注册的核心服务:

服务接口生命周期说明
缓存IHmxCacheSingleton本地内存缓存
分布式缓存IHmxDistributedCacheSingletonRedis分布式缓存
对象映射IObjectMapperSingleton对象转换
类型查找ITypeFinderSingleton程序集扫描
配置IConfigurationSingleton配置访问
模块管理IHiMindModuleManagerSingleton模块生命周期
HTTP上下文IHttpContextAccessorScopedHTTP上下文访问
数据门户IDataPortalClient-远程服务调用

最佳实践

1. 根据服务类型选择注入方式

服务类型推荐方式原因
AppService(实现 IHmxAppServiceEngineContext.Current.Resolve动态WebAPI机制决定
普通服务(不实现 IHmxAppService构造函数注入标准DI实践

2. 合理选择生命周期

场景推荐生命周期原因
无状态服务Singleton性能最优
有状态/数据库操作Scoped避免线程安全问题
轻量级工具Transient按需创建

3. 避免循环依赖

csharp
// ❌ 错误:循环依赖
public interface IServiceA : ISingletonDependency
{
    void DoWork(IServiceB b);
}

public interface IServiceB : ISingletonDependency
{
    void DoWork(IServiceA a);
}

// ✅ 正确:通过中介或重构解决
public interface IMessageBus : ISingletonDependency
{
    void Publish<T>(T message);
}

4. 接口与实现分离

csharp
// ✅ 推荐:接口和实现分开定义
public interface ICacheService : ISingletonDependency
{
    T Get<T>(string key);
}

public class RedisCacheService : ICacheService
{
    // 实现...
}

// ❌ 避免:直接注册实现类
services.AddSingleton<RedisCacheService>();

常见问题

AppService 中能否使用构造函数注入?

答:可以使用,但不推荐。

AppService 类(实现 IHmxAppService 的类)被框架的动态WebAPI机制识别为ASP.NET Core的Controller,技术上支持构造函数注入

csharp
// ✅ 技术上可行
public class AdminAppService : HmxAppServiceBase<AdminAppService>, IAdminAppService
{
    private readonly IMyService _myService;
    
    // 可以添加构造函数注入
    public AdminAppService(IMyService myService)
    {
        _myService = myService;
    }
}

但不推荐的原因:

原因说明
模式混用基类用EngineContext,子类用构造函数注入,风格不统一
依赖不透明Logger、Cache等基类依赖隐藏在属性中,新依赖通过构造函数注入,维护时容易混淆
现有代码惯例项目中的AppService类都没有使用构造函数注入

推荐写法:

csharp
// ✅ 推荐:使用 EngineContext.Current.Resolve
public class AdminAppService : HmxAppServiceBase<AdminAppService>, IAdminAppService
{
    private IMyService GetMyService()
    {
        return EngineContext.Current.Resolve<IMyService>();  // ✅ 与基类风格一致
    }
}

服务无法解析

症状:运行时抛出 InvalidOperationException: No service for type

排查

  1. 确认服务是否已注册(标记接口或手动注册)
  2. 检查生命周期是否匹配(Scoped服务不能在Singleton中注入)
  3. 查看模块是否正确加载

服务生命周期错误

症状Cannot consume scoped service from singleton

原因:在Singleton服务中注入了Scoped服务

解决

csharp
// ❌ 错误
public class SingletonService
{
    public SingletonService(IScopedService scoped) { } // 错误!
}

// ✅ 正确:使用 IServiceScopeFactory
public class SingletonService
{
    private readonly IServiceScopeFactory _scopeFactory;
    
    public SingletonService(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }
    
    public void DoWork()
    {
        using var scope = _scopeFactory.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
        scopedService.DoWork();
    }
}

自动注册不生效

检查项

  1. DLL是否在应用程序运行目录
  2. 类是否为非抽象、非泛型的具体类
  3. 是否正确继承标记接口
  4. 程序集是否被排除前缀过滤

HiMind 工业互联网平台 技术文档