OpenRobot.Framework.Modularity
1.0.0
dotnet add package OpenRobot.Framework.Modularity --version 1.0.0
NuGet\Install-Package OpenRobot.Framework.Modularity -Version 1.0.0
<PackageReference Include="OpenRobot.Framework.Modularity" Version="1.0.0" />
<PackageVersion Include="OpenRobot.Framework.Modularity" Version="1.0.0" />
<PackageReference Include="OpenRobot.Framework.Modularity" />
paket add OpenRobot.Framework.Modularity --version 1.0.0
#r "nuget: OpenRobot.Framework.Modularity, 1.0.0"
#:package OpenRobot.Framework.Modularity@1.0.0
#addin nuget:?package=OpenRobot.Framework.Modularity&version=1.0.0
#tool nuget:?package=OpenRobot.Framework.Modularity&version=1.0.0
OpenRobot.Framework.Modularity
模块化启动框架(net8.0):模块生命周期 + [DependsOn] 拓扑排序 + 程序集扫描 + 系统路径配置 + 后台定时任务。
日志基于 Microsoft.Extensions.Logging,依赖注入用标准 IServiceCollection。
- 模块化:一个模块 = 一个继承
StartupModule的类;四阶段PreInitialize → Initialize → PostInitialize → Shutdown, 外加异步就绪等待WaitReadyAsync。依赖关系用[DependsOn]声明,框架做确定性拓扑排序。 - 构造注入:模块由容器经
ActivatorUtilities创建,要日志注入ILogger<TModule>,要服务注入接口—— 没有静态服务定位器。 - 依赖只读:模块由容器创建、容器只构筑一次;不提供运行期改容器的能力。
- 无副作用:不写环境变量、不落盘、不起后台线程(后台任务要显式注册才会跑)。
安装
<PackageReference Include="OpenRobot.Framework.Modularity" Version="1.0.0" />
包由本仓库产出:dotnet pack common/OpenRobot.Framework.Modularity/OpenRobot.Framework.Modularity.csproj -c Release。
依赖:
Microsoft.Extensions.DependencyInjection8.0.1(完整包:BuildServiceProvider()不在 Abstractions 里)Microsoft.Extensions.Logging.Abstractions8.0.2
库不引具体日志实现。控制台输出由宿主引
Microsoft.Extensions.Logging.Console并调AddSimpleConsole()决定。
快速开始
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenRobot.Framework.Modularity;
using OpenRobot.Framework.Modularity.BackgroundTasks;
using OpenRobot.Framework.Modularity.Paths;
var builder = ModuleHostBuilder.Create(args);
builder.Services.AddLogging(b => b
.AddSimpleConsole(o => { o.SingleLine = true; o.TimestampFormat = "HH:mm:ss.fff "; })
.SetMinimumLevel(LogLevel.Information));
builder.Services.AddSingleton<ISystemPathConfiguration>(new SystemPathConfiguration());
builder.Services.AddSingleton<IBackgroundTaskManager, BackgroundTaskManager>();
builder.Services.AddSingleton<MyService>();
builder.Options.AssemblyPrefixes.Add("MyApp"); // 主动加载 MyApp*.dll 里的模块
builder.AddModule<BackgroundTasksModule>(); // 想要后台任务统一启停就加这一行
await using var host = builder.Build(); // 容器在这里构筑,且只构筑这一次
await host.RunAsync(); // 初始化 → 就绪等待 → 阻塞至 Ctrl+C → 逆序关闭
写一个模块
public sealed class MyModule : StartupModule
{
private readonly ILogger<MyModule> _logger;
private readonly MyService _service;
// 构造注入:容器负责解析;模块本身不必注册进 services
public MyModule(ILogger<MyModule> logger, MyService service)
{
_logger = logger;
_service = service;
}
public override void Initialize()
{
// 只注册、不连接——此刻其它模块的处理器可能还没就位
_service.MessageReceived += OnMessage;
_logger.LogInformation("MyModule 已装配");
}
public override void PostInitialize()
{
_service.Connect(); // 全部模块 Initialize 完成后才发起连接
}
public override async Task WaitReadyAsync(CancellationToken ct)
{
await _service.WaitConnectedAsync(ct); // 就绪前不打印「服务已启动」
}
public override void Shutdown() => _service.MessageReceived -= OnMessage;
}
声明依赖
[DependsOn(typeof(DeviceRegisterModule))] // 本模块排在 DeviceRegisterModule 之后
public sealed class CloudMqttModule : StartupModule { /* ... */ }
API 速查
ModuleHostBuilder
| 成员 | 说明 |
|---|---|
Create(args) |
创建构建器;args 存进 Options.Arguments,库不解析 |
Services |
IServiceCollection,用标准扩展方法注册 |
Options |
ModuleHostOptions(扫描前缀 / 就绪超时 / Ctrl+C) |
AddModule<TModule>() |
显式注册模块类型(不在扫描范围或需要固定顺序时用) |
Build() |
构筑容器 + 收集模块(显式 ∪ 扫描)→ 拓扑排序 → 实例化,返回 ModuleHost |
ModuleHost
| 成员 | 说明 |
|---|---|
Services / Modules |
容器 / 按拓扑序的模块实例 |
Initialize() |
PreInitialize → Initialize → PostInitialize,幂等 |
WaitReadyAsync(ct) |
按序调各模块就绪等待;单个超时/异常不阻断其余 |
RunAsync(ct) |
完整流程:初始化 → 就绪 → 阻塞至 Ctrl+C / ct → 关闭 |
Shutdown() |
逆序关闭,逐模块隔离异常,幂等 |
DisposeAsync() |
Shutdown + 释放容器 |
ModuleHostOptions
| 项 | 默认 | 说明 |
|---|---|---|
AssemblyPrefixes |
空 | 扫描前主动加载的 dll 名前缀(不区分大小写)。空 = 只扫已加载程序集 |
ReadyTimeout |
15s |
就绪等待整体超时;超时后后台初始化继续 |
HandleConsoleCancelKeyPress |
true |
RunAsync 是否接管 Ctrl+C |
Arguments |
启动参数 | 原样保存,库不解析 |
StartupModule
| 成员 | 说明 |
|---|---|
PreInitialize / Initialize / PostInitialize / Shutdown |
四个虚方法,默认空实现 |
WaitReadyAsync(ct) |
异步就绪等待,默认立即就绪 |
Services |
宿主容器。优先用构造注入,本属性只是延迟解析的逃生口 |
BackgroundTasks
| 类型 | 说明 |
|---|---|
IBackgroundTask |
Name + Start() + IAsyncDisposable |
BackgroundTaskBase |
周期任务基类:启动即执行一次,之后按 Interval 循环;异常不中断循环 |
IBackgroundTaskManager |
All / Register(不启动)/ StartAll(幂等)/ StopAll(逆序 + 清空) |
BackgroundTasksModule |
内置模块:PostInitialize 统一启动、Shutdown 统一停止 |
public sealed class HeartbeatTask : BackgroundTaskBase
{
public HeartbeatTask(ILoggerFactory factory, MyService service) : base(factory) => _service = service;
public override string Name => "心跳";
public override TimeSpan Interval => TimeSpan.FromSeconds(30);
protected override Task ExecuteAsync(CancellationToken ct) => _service.PingAsync(ct);
}
// 模块 Initialize 里注册,BackgroundTasksModule 在 PostInitialize 统一启动
_manager.Register(new HeartbeatTask(loggerFactory, service));
Paths
| 成员 | 值 |
|---|---|
AppRootPath |
构造参数;默认 AppContext.BaseDirectory |
AppDataPath / AppLogPath / PluginPath |
{root}/Data、{root}/Logs、{root}/Plugins |
与源工程对照
迁移自 SmartEdge 的 OpenRobot.EdgeControl.* / SmartStudio.SelfHost.Startup:
| 源工程 | 本库 | 变化 |
|---|---|---|
StartupModule |
StartupModule |
去掉 Configuration 属性(改构造注入) |
SmartStartup |
ModuleHost + ModuleHostBuilder |
静态类 → 实例类;扫描前缀从硬编码改为可配 |
ISmartLogger / ConsoleLogger |
删除 | 换 Microsoft.Extensions.Logging |
SmartConfiguration |
删除 | 换标准 DI |
StartupModuleConfig |
删除 | 「日志 + 系统路径」打包体,两者现由 DI 直接提供 |
HostStartupModule |
BackgroundTasksModule |
同样职责,收进库内 |
SystemPathConfiguration |
Paths.SystemPathConfiguration |
增加可注入的根目录(便于测试) |
三处行为差异(有意为之)
- 循环依赖抛异常:源实现检测到环后把剩余模块按发现顺序追加(顺序随机且无人知晓)。
本库在
Build()抛InvalidOperationException,消息含涉及模块名。 - Shutdown 逐模块隔离:源实现任一模块抛异常即中断逆序链,后续模块永不释放。本库逐个 try/catch 记日志继续。
- Initialize 失败回滚:源实现
Initialize()抛出后已初始化模块不回收。本库捕获后逆序 Shutdown 已进入 Initialize 的模块再重抛,不留半初始化状态。
确定性拓扑排序
源工程按 AppDomain.GetAssemblies() 的返回顺序建图——同层模块的先后由运行时决定,多机部署可能出现
「本机正常、另一台启动顺序不同」。本库先把节点按 (程序集名, 类型全名) 定序再排序,相同输入永远给出相同顺序。
已知限制
- 容器必须异步释放:
ModuleHost内含只实现IAsyncDisposable的服务时,同步Dispose()会抛InvalidOperationException。始终写await using var host = builder.Build();。 - 容器构筑后冻结:不提供运行期增删服务的能力(源工程
SmartConfiguration的反复重建容器会导致 旧 provider 泄漏、已解析单例丢失)。运行期可变状态请自己持有字段或用模块间事件。 [DependsOn]指向未参与构建的类型会被忽略:否则引一个可选模块就得连带引它的整个依赖程序集。- 框架自带模块不参与自动扫描:
BackgroundTasksModule位于框架程序集,扫描时被跳过—— 自动纳进来会让「没注册IBackgroundTaskManager」的宿主在Build()里直接抛(编译通过、启动崩溃)。 要用后台任务统一启停,请显式AddModule<BackgroundTasksModule>()并注册IBackgroundTaskManager。 扫描的语义是「发现应用自己的模块」,与源工程一致(HostStartupModule本就在应用程序集里)。 PreInitialize阶段不参与回滚:若某模块在PreInitialize抛出,已执行PreInitialize的模块不会被Shutdown(它们的Initialize尚未开始,通常也没有资源在手)。需要资源保护的初始化放Initialize。
版本历史
1.0.0(首次发布)
- 模块生命周期
PreInitialize / Initialize / PostInitialize / WaitReadyAsync / Shutdown [DependsOn]确定性拓扑排序,循环依赖抛异常ModuleHostBuilder/ModuleHost:容器一次性构筑、模块经ActivatorUtilities构造注入- 程序集扫描(可配前缀,主动加载未加载的 dll;
ReflectionTypeLoadException降级取可用类型) ISystemPathConfiguration/SystemPathConfigurationBackgroundTaskBase/IBackgroundTaskManager/BackgroundTasksModule- 日志改用
Microsoft.Extensions.Logging,不再有自定义日志抽象
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on OpenRobot.Framework.Modularity:
| Package | Downloads |
|---|---|
|
OpenRobot.Litegraph.Core
Litegraph 流程引擎核心:画布解析与校验、图模型、46 个内置节点处理器、同步/异步通道、触发流程与主流程 tick、执行记录诊断。 |
|
|
OpenRobot.Edge.AppSdk
边缘应用接入 SDK:连接 EdgeWebApi、自动注册、配置声明与热更新、状态上报、业务数据写入。应用只写业务逻辑,配置/存储/控制面交给平台。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 120 | 9/18/2026 |