Known 的可扩展性建立在"程序集即插件"这个约定上:一个模块就是一个类库,
Config.AddModule<T>() 只需一个泛型锚点,就能把整个程序集的路由、服务与元数据接进运行时。
反射扫描的三条线
调用 AddModule<T>() 时,Known 通过 typeof(T).Assembly 拿到目标程序集,然后按三条线扫描:
- Razor 组件 — 所有
@page指令生成的组件类型加入Router.AdditionalAssemblies, 跨程序集的路由与本项目内的路由共用一张表。 - 服务契约 — 类接口带
[WebApi]特性时注册到服务端路由;类实现带[Service]特性时 注册到 DI 容器并自动配对到对应接口。 - 元模型工件 — 领域内的 Schema/Synonym/Entity 定义按
SysSchemaItem多态工件表登记, 运行时以SysMenu薄导航树组织可见性。
三条线共享同一份程序集清单,因此模块的"接入点"只有一处、心智负担恒定。
为什么不用 [Module] 特性
早期版本曾在 assemblyinfo 上标 [assembly: KnownModule(...)]。定稿改回
AddModule<T>() 显式调用,原因有三:
- 可排序:程序启动顺序显式、可预测,特性驱动会有加载顺序歧义。
- 可裁剪:同一份模块程序集,可以按需只
AddModule<Frontend>()或只AddModule<Backend>()。 - 可测试:单元测试里创建
ServiceCollection后手动AddModule<T>()即可,不依赖特性扫描副作用。
模块程序集的最小骨架
MyCompany.Known.Modules.Order/
├─ MyCompany.Known.Modules.Order.csproj Razor 类库
├─ _Imports.razor
├─ OrderModule.cs // 用作 AddModule<T> 锚点的空标记类
├─ Pages/
│ └─ OrderList.razor // @page "/order/list"
├─ Services/
│ └─ OrderService.cs // IOrderService 接口([WebApi]) 与 OrderService 实现([Service]) 同文件
└─ wwwroot/
└─ order.css // 模块私有样式,_content 路径自动可访问
宿主端接入:
Config.AddModule<OrderModule>();
边界与限制
- 不要跨模块引用 Razor 组件;模块间应通过
[WebApi]契约或元模型工件交流。 - 不要把业务实体的 EF
DbContext直接放在模块里;数据层属宿主进程的职责,见数据层。 - 模块程序集必须是 Razor 类库(
Microsoft.NET.Sdk.Razor);纯类库无法输出_content/静态资源路径。 - 一个
T只能对应一个程序集,AddModule<T>()重复调用会去重。
与其他概念的关系
- 装配流程见 AddKnown 三方法。
- 模块间通信与前后端强类型走 ServiceProxy 客户端代理。
Config.*静态门面的可配置项见 配置项 / 属性全表。
扫码关注公众号
扫码加入 QQ 群