# EasyCore.ApplicationModule **Repository Path**: wzhy-0521/easy-core.-application-module ## Basic Information - **Project Name**: EasyCore.ApplicationModule - **Description**: EasyCore.ApplicationModule - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-07 - **Last Updated**: 2026-07-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 🧩 EasyCore.ApplicationModule > **EasyCore.ApplicationModule** 是面向 .NET 8 / ASP.NET Core 的模块化启动内核。通过 `[RequiresModule]` 声明依赖、拓扑排序加载模块,并提供完整的 Configure / Initialize / Shutdown 生命周期钩子——让 Domain / Application / WebApi 等分层模块以可预测的顺序组装应用。 ![.NET](https://img.shields.io/badge/.NET-8.0-512BD4?logo=dotnet) ![C#](https://img.shields.io/badge/C%23-12-239120?logo=csharp) ![ASP.NET Core](https://img.shields.io/badge/ASP.NET%20Core-Modular-blueviolet) ![Lifecycle](https://img.shields.io/badge/Lifecycle-8%20Hooks-orange) ![License](https://img.shields.io/badge/License-MIT-yellow) ![Version](https://img.shields.io/badge/Version-8.3.0-blue) --- ## 🌍 Language - **中文(当前文档)** - English: [README.en.md](https://github.com/RockyWang0521/EasyCore.ApplicationModule/blob/master/README.en.md) --- ## 📚 目录 ### 第一部分:总览与架构 - [1. 项目定位](#1-项目定位) - [2. 架构与模块关系](#2-架构与模块关系) - [3. NuGet / 项目清单](#3-nuget--项目清单) - [4. 核心概念对照](#4-核心概念对照) ### 第二部分:快速上手 - [5. 环境要求](#5-环境要求) - [6. 安装](#6-安装) - [7. 三分钟快速开始](#7-三分钟快速开始) - [8. API 与类型完整说明](#8-api-与类型完整说明) ### 第三部分:生命周期 · 依赖 · Demo - [9. 生命周期详解](#9-生命周期详解) - [10. RequiresModule 与加载顺序](#10-requiresmodule-与加载顺序) - [11. Context 上下文](#11-context-上下文) - [12. 三层 Demo 项目](#12-三层-demo-项目) ### 第四部分:迁移与生产 - [13. 从旧版迁移](#13-从旧版迁移) - [14. 生产清单](#14-生产清单) - [15. FAQ](#15-faq) - [16. License](#16-license) --- ## 1. 项目定位 EasyCore.ApplicationModule 解决「在 ASP.NET Core 里按模块组装启动」的问题: | 痛点 | EasyCore.ApplicationModule 做法 | |---|---| | `Program.cs` 臃肿、分层边界模糊 | 每层一个 `AppModule`,职责清晰 | | 依赖顺序靠约定、易写错 | `[RequiresModule]` + 拓扑排序,依赖必先于依赖方 | | 全目录 DLL 盲扫不安全 | 显式 `Run`,只加载启动模块闭包 | | 缺少统一生命周期 | 8 个钩子覆盖 Configure → Init → Shutdown | | 循环依赖难发现 | 加载期检测并抛出清晰异常 | ### 1.1 设计原则 | 原则 | 说明 | |---|---| | **显式启动** | 入口模块类型由泛型参数指定,无魔法扫描 | | **依赖可声明** | `[RequiresModule]` 支持多特性、基类继承 | | **顺序可预测** | 同层按 `FullName` 稳定排序;Shutdown 逆序 | | **上下文完备** | Configure / Application / Shutdown 三类 Context | | **分层友好** | Demo 标准三层:Domain / Application / WebApi | ### 1.2 解决方案目录 ```text EasyCore.ApplicationModule/ ├── src/ │ └── EasyCore.ApplicationModule/ # 核心:Loader / Manager / Lifecycle ├── demo/ │ ├── Demo.Domain/ # 领域层模块 │ ├── Demo.Application/ # 应用层模块 │ └── Demo.WebApi/ # 宿主 + Infrastructure 模块 ├── tests/EasyCore.ApplicationModule.Tests/ └── docs/svg/ # README 架构图 ``` --- ## 2. 架构与模块关系 ### 2.1 组件关系图 ![architecture-cn](https://cdn.jsdelivr.net/gh/RockyWang0521/EasyCore.ApplicationModule@master/docs/svg/architecture-cn.svg) ### 2.2 生命周期 ![lifecycle-cn](https://cdn.jsdelivr.net/gh/RockyWang0521/EasyCore.ApplicationModule@master/docs/svg/lifecycle-cn.svg) ### 2.3 启动数据流(文字版) ```text Run │ ▼ ModuleLoader ──► 收集 RequiresModule 闭包 │ ▼ 拓扑排序 + 环检测 ──► ModuleDescriptor[] │ ▼ ModuleManager │ ├─ Pre / Configure / PostConfigureServices(ConfigureContext) ├─ Build WebApplication ├─ OnPre / On / OnPostApplicationInitialization(ApplicationContext) └─ OnApplicationShutdown(ShutdownContext) ← 逆序,ApplicationStopping ``` --- ## 3. NuGet / 项目清单 | 包 / 项目 | 职责 | 是否必须 | |---|---|---| | `EasyCore.ApplicationModule` | 模块内核、加载器、生命周期 | ✅ | | `Demo.Domain` | 领域实体与仓储(示例) | Demo | | `Demo.Application` | 应用服务与 DTO(示例) | Demo | | `Demo.WebApi` | 宿主、Swagger、API(示例) | Demo | | `EasyCore.ApplicationModule.Tests` | 单元测试 | 开发 | ```bash dotnet add package EasyCore.ApplicationModule ``` --- ## 4. 核心概念对照 | 类型 | 角色 | |---|---| | `IAppModule` | 模块契约(7 个生命周期方法) | | `AppModule` | 抽象基类,空虚方法默认实现 | | `[RequiresModule]` | 声明依赖模块(可多特性、可继承) | | `ModuleLoader` | 加载闭包 + 拓扑排序 | | `ModuleDescriptor` | 模块类型 / 实例 / 直接依赖 | | `ModuleManager` | 按序调用钩子;Shutdown 逆序 | | `ConfigureContext` | 服务配置阶段上下文 | | `ApplicationContext` | 应用初始化阶段上下文 | | `ShutdownContext` | 关闭阶段上下文 | | `EasyCoreApplicationModule.Run` | 宿主入口 | --- ## 5. 环境要求 | 项 | 要求 | |---|---| | .NET | 8.0+ | | 宿主 | ASP.NET Core(`WebApplication`) | | 模块类型 | 具体类,实现 `IAppModule`,公共无参构造 | --- ## 6. 安装 ```bash dotnet add package EasyCore.ApplicationModule ``` 或项目引用: ```xml ``` --- ## 7. 三分钟快速开始 ### 7️⃣.1️⃣ 定义模块 ```csharp using EasyCore.ApplicationModule; public class CoreModule : AppModule { public override void ConfigureServices(ConfigureContext context) { context.Services.AddControllers(); context.Services.AddEndpointsApiExplorer(); } } [RequiresModule(typeof(CoreModule))] public class WebHostModule : AppModule { public override void ConfigureServices(ConfigureContext context) { context.Services.AddSwaggerGen(); } public override void OnApplicationInitialization(ApplicationContext context) { if (context.App.Environment.IsDevelopment()) { context.App.UseSwagger(); context.App.UseSwaggerUI(); } context.App.MapControllers(); } } ``` ### 7️⃣.2️⃣ 一行启动 ```csharp // Program.cs using EasyCore.ApplicationModule; EasyCoreApplicationModule.Run(args); ``` ### 7️⃣.3️⃣ 运行三层 Demo ```bash dotnet run --project demo/Demo.WebApi # 浏览器打开 http://localhost:5037/swagger # 调用 GET /api/products ``` 控制台可看到各层 `PreInit / Init / PostInit` 日志,验证依赖顺序。 --- ## 8. API 与类型完整说明 | API / 类型 | 说明 | |---|---| | `EasyCoreApplicationModule.Run(args)` | 加载模块 → 配置服务 → Build → 初始化 → `app.Run()` | | `ModuleLoader.LoadModules()` / `LoadModules(Type)` | 返回依赖序 `IReadOnlyList` | | `ModuleManager` | `PreConfigureServices` … `OnApplicationShutdown` | | `[RequiresModule(params Type[])]` | 可重复标注;从当前类型向上读取基类特性 | | `ModuleDescriptor.Type` / `Instance` / `Dependencies` | 元数据与直接依赖 | | `EasyCoreAppRun` | **已废弃**;抛出 `NotSupportedException`,请改用 `Run` | 模块必须是**具体** `IAppModule` 实现,且具备**公共无参构造函数**。 --- ## 9. 生命周期详解 | # | 钩子 | 时机 | 典型用途 | |---|---|---|---| | 1 | `PreConfigureServices` | Build 前 | 选项早期绑定、服务替换 | | 2 | `ConfigureServices` | Build 前 | 注册 DI、中间件相关服务 | | 3 | `PostConfigureServices` | Build 前 | 最终覆盖、校验 | | 4 | — | `builder.Build()` | 构建 `WebApplication` | | 5 | `OnPreApplicationInitialization` | Build 后 | 管道前准备 | | 6 | `OnApplicationInitialization` | Build 后 | `Use*` / `Map*` | | 7 | `OnPostApplicationInitialization` | `ApplicationStarted` | 启动后通知、预热 | | 8 | `OnApplicationShutdown` | `ApplicationStopping` | 释放资源(**逆依赖序**) | > 同一钩子内:按拓扑序,依赖模块先于依赖方;Shutdown 相反。 --- ## 10. RequiresModule 与加载顺序 ### 10.1 声明依赖 ```csharp // 单个 [RequiresModule(typeof(DemoDomainModule))] public class DemoApplicationModule : AppModule { } // 多个特性(并集) [RequiresModule(typeof(DemoApplicationModule))] [RequiresModule(typeof(DemoInfrastructureModule))] public class DemoWebApiModule : AppModule { } // 基类上的特性也会被读取 [RequiresModule(typeof(CoreModule))] public class BaseWebModule : AppModule { } public class DerivedWebModule : BaseWebModule { } // 仍依赖 CoreModule ``` ### 10.2 Demo 依赖图 ![demo-layers-cn](https://cdn.jsdelivr.net/gh/RockyWang0521/EasyCore.ApplicationModule@master/docs/svg/demo-layers-cn.svg) ### 10.3 环检测 ```csharp [RequiresModule(typeof(CycleBModule))] public class CycleAModule : AppModule { } [RequiresModule(typeof(CycleAModule))] public class CycleBModule : AppModule { } // ModuleLoader.LoadModules() // → InvalidOperationException: Circular module dependency... ``` --- ## 11. Context 上下文 ### 11.1 ConfigureContext(服务配置阶段) | 属性 | 说明 | |---|---| | `Services` | `IServiceCollection` | | `Configuration` | `IConfiguration` | | `Environment` | `IHostEnvironment` | | `Builder` | `WebApplicationBuilder`(可改 Logging 等) | ### 11.2 ApplicationContext(初始化阶段) | 属性 | 说明 | |---|---| | `App` | 已构建的 `WebApplication` | | `ServiceProvider` | 根 `IServiceProvider`(同 `App.Services`) | ### 11.3 ShutdownContext(关闭阶段) | 属性 | 说明 | |---|---| | `Provider` | 根 `IServiceProvider` | --- ## 12. 三层 Demo 项目 ![demo-layers-cn](https://cdn.jsdelivr.net/gh/RockyWang0521/EasyCore.ApplicationModule@master/docs/svg/demo-layers-cn.svg) | 项目 | 模块 | 依赖 | 职责 | |---|---|---|---| | [`Demo.Domain`](demo/Demo.Domain) | `DemoDomainModule` | — | `Product`、`IProductRepository`、内存仓储 | | [`Demo.Application`](demo/Demo.Application) | `DemoApplicationModule` | Domain | `IProductAppService`、DTO | | [`Demo.WebApi`](demo/Demo.WebApi) | `DemoWebApiModule` + `DemoInfrastructureModule` | Application + Infra | Controllers、Swagger、宿主入口 | ```bash dotnet run --project demo/Demo.WebApi ``` | 端点 | 说明 | |---|---| | `GET /api/products` | 产品列表 | | `GET /api/products/{id}` | 按 Id 查询 | | `POST /api/products` | 创建产品 | | `/swagger` | Swagger UI(Development) | 每个 Demo 模块都重写了**全部 7 个生命周期钩子**,便于在控制台观察启动顺序。 --- ## 13. 从旧版迁移 **8.0.0** 相对早期「扫描 BaseDirectory 全部 DLL」版本为破坏性升级: | 旧版 | 8.0 | |---|---| | `EasyCoreAppRun(args)` 盲扫 DLL | `Run(args)` | | 隐式发现所有 `AppModule` | 仅启动模块 + `[RequiresModule]` 闭包 | | 依赖顺序不明确 | 拓扑排序 + 环检测 | | License | **MIT** | 迁移步骤: 1. 为每层创建 `XxxModule : AppModule` 2. 用 `[RequiresModule]` 声明依赖 3. 将 `Program.cs` 改为 `EasyCoreApplicationModule.Run(args)` 4. 删除对已废弃 `EasyCoreAppRun` 的调用 --- ## 14. 生产清单 - [ ] 入口只指定一个明确的 Host / WebApi 模块 - [ ] 跨项目依赖用 `[RequiresModule]` 声明,避免「碰巧」的加载顺序 - [ ] 管道配置集中在 `OnApplicationInitialization` - [ ] 释放逻辑写在 `OnApplicationShutdown`(注意逆序) - [ ] 模块类保持无参构造;不要在构造里访问 DI - [ ] 循环依赖在本地用单元测试提前暴露(见 `tests`) - [ ] 生产关闭不必要的 Demo 日志,保留关键生命周期日志即可 --- ## 15. FAQ **Q: 为什么不用扫描插件目录?** A: 显式启动更安全、可预测。需要插件化时可在宿主模块内自行加载程序集后再声明依赖。 **Q: Shutdown 为什么逆序?** A: 依赖方往往占用依赖方资源;后启动的先关闭,避免依赖已被释放。 **Q: 可以在模块构造函数里注入服务吗?** A: 不可以。模块由 `Activator.CreateInstance` 创建,需公共无参构造;服务请在钩子内通过 Context 获取。 **Q: 多个 `[RequiresModule]` 和写在一个特性里有何区别?** A: 无功能差异,都是依赖并集。可按可读性选择。 **Q: `OnPostApplicationInitialization` 何时触发?** A: 挂在 `ApplicationStarted` 上,应用真正开始监听之后。 **Q: Demo 端口是多少?** A: 默认 `http://localhost:5037`(见 `launchSettings.json`)。 --- ## 16. License MIT — 详见 [LICENSE](LICENSE)。 --- ## 🤝 贡献 1. Fork 并创建特性分支 2. 在 `tests/EasyCore.ApplicationModule.Tests` 补充测试 3. 执行 `dotnet test` 与 `dotnet build EasyCore.ApplicationModule.sln` 4. 提交 Pull Request 欢迎 Issue / PR 🚀 --- ## 🔗 仓库 https://github.com/RockyWang0521/EasyCore.ApplicationModule