项目结构导览

对照开源的 KnownApp / KnownWasm / KnownWeb / KnownWin,讲清 Program.cs、App.razor、Pages、Services 各自的分担。

最后更新

跑起来之后先看一眼你手上有什么。Known 的开源仓库刻意拆成"一个核心库 + 一个业务插件示范 + 三个宿主",边界非常干净:核心库不做业务、业务插件不选宿主、宿主不重复装配。这一页把每个目录谁负责什么讲清楚。

顶层目录

Known/
├── Known/              核心 RCL(Razor 类库)
├── KnownApp/           业务插件示例:装配自封装、页面、服务、数据
├── KnownWasm/          纯前端宿主(BlazorWebAssembly)
├── KnownWeb/           全栈一体宿主(ASP.NET Core + WASM hosted)
├── KnownWin/           桌面宿主(WinForms + BlazorWebView)
├── Known.Cli/          dotnet 全局工具 known(项目脚手架,本文档暂不展开)
├── Known.CodeGen/      代码骨架生成(供 Known.Cli 调用,暂不展开)
├── Known.Demo/         在线示例集(组件详情页的"能改能跑"卡片)
└── Known.slnx          解决方案

Known/ · 核心 RCL

这是唯一的框架本体,也是所有其它工程唯一直接引用的 NuGet / ProjectReference。目录组织按能力切:

Known/
├── App.razor / App.razor.cs      根组件:<Router AppAssembly="Config.App.Assembly" AdditionalAssemblies="Config.Assemblies">
├── Config.cs                     静态装配门面 + AppInfo / ClientOption / ServerOption / MenuGroup / TopAction
├── Attributes.cs                 [WebApi] / [Service] / [Menu] / [AppMenu] / [Column]
├── AuthProvider.cs               IAuthProvider 契约 + 默认 AuthProvider(进程内用户 + 特性扫描装配)
├── ServiceProxy.cs               DispatchProxy 生成的强类型 HTTP 门面
├── Entity.cs                     BaseEntity / EntityBase(Id、CreateBy/CreateTime、ModifyBy/ModifyTime…)
├── Result.cs                     Result / PagingCriteria / PagingResult<T>
├── Logger.cs                     文件日志(按天 + 按大小滚动)
├── Components/                   112 个组件(AI / Base / Data / Devices / Feedback / Form / Layout / Media / Page)
│   ├── BaseComponent.cs          所有组件的基类:Class / Style / Disabled / Visible / UserAttributes + UI/Local 注入
│   ├── UIService.cs              全局消息门面:Toast / Notification / LoadingBar / Modal / Drawer
│   ├── LocalService.cs           本地化 + 主题 + 语言 + 密度 + 强调色
│   └── IconData.cs               内联 SVG 图标字典
├── Data/                         ADO.NET 抽象 + 八种方言 + Schema 同步
│   ├── Database.cs               Query / Execute / Page / Trans / Scalar / Schema 分部类
│   ├── DatabaseOption.cs         AddSQLite / AddSQLServer / AddMySQL / AddPostgreSQL / AddOracle / AddDM / AddAccess
│   └── DbProvider.*.cs           各方言适配(分页语法、日期函数、布尔字面量)
├── Models/                       业务侧公用 DTO(UserInfo / LoginInfo / TraceLog / 各媒体状态…)
├── Cache/                        EntityCache / PropertyCache(反射元数据缓存)
├── Extensions/                   Type / Dictionary / DataTable / UI / Http 扩展
├── Helpers/                      Snowflake / CsvHelper
└── Platforms/                    AuthService / LanguageService / OnlineTracker / SysConfig / SysLanguage / DevLanguages / DevOnlineUsers / DevTraceLogs

Config.AddModule<T>() 是唯一入口,扫到的 [WebApi] [Service] [Menu] [AppMenu] [Table] 全部记在 Config 的静态列表里;AddKnownClient / AddKnownWeb / AddKnownDesktop 再据此生成 HTTP 代理、端点映射或本地 DI 绑定。核心库不做具体业务,Known/Platforms/AuthService.cs 那种默认实现只是给你参考、可以完全覆盖。

KnownApp/ · 业务插件示例

这是你写自己业务代码时该长成什么样的样板。它的核心不是页面,是 AppConfig.cs——把整个应用的启动装配沉在这里,暴露成一行 AddKnownApp(AppType) 给宿主调用:

namespace KnownApp;

public static class AppConfig
{
    public static string AppId => "Known";
    public static string AppName => "Known信息管理系统";

    public static void AddKnownApp(this IServiceCollection services, AppType type)
    {
        var assembly = typeof(AppConfig).Assembly;

        services.AddKnown(option =>
        {
            option.Id = AppId;
            option.Name = AppName;
            option.Type = type;
            option.Assembly = assembly;
            option.CreateTime = new DateTime(2026, 10, 1);
        });

        // 放到 AddKnown 后面,可以覆盖 AuthService 的默认实现
        Config.AddModule(assembly);

        // 菜单分组集中声明一次,页面 [Menu].Group 按 id 引用
        Config.AddMenuGroup("components", "组件示例", icon: "package", order: 50);
        Config.AddMenuGroup("system",     "系统管理", icon: "sliders", order: 90);

        // 组件菜单右侧挂徽标:数量取自 DemoCatalog(唯一真相源)
        Config.AddMenuDecorator(item => { /* 按 Url 匹配组件一级页,写 item.Badge */ });

        // 顶栏右上角自定义功能:Config.AddTopAction(...)
    }

    public static async Task SeedAsync(Database db) { /* 演示初始数据(幂等) */ }
}

三件事一次声明完:元信息(AppInfo)、模块扫描(Config.AddModule)、布局元数据(分组、装饰器、顶栏)。宿主不需要重复配置。

KnownApp/
├── AppConfig.cs          上面这段:AddKnownApp + SeedAsync
├── Apps/                 手机端页面:AppIndex.razor(登录后首页)
├── Pages/                电脑端页面(Index / Login / Components/{每组一目录})
├── Services/             业务服务实现(AuthService.cs 等,标记 [Service] 覆盖框架默认)
├── Demos/                示例数据(组件在线示例的 seed 内容)
├── _Imports.razor(.cs)   命名空间批量导入,页面少写 using
└── wwwroot/              本插件的静态资源 / 语言包 i18n/{tag}.json

Services/AuthService.cs 会带 [Service],Config.AddModule(assembly) 扫描时把它登记;AddKnownWeb 或 AddKnownDesktop 之后,IAuthService 从 DI 解析到的就是它、不再是框架默认——这就是"业务插件覆盖框架默认"的机制,也是 AddModule 要放在 AddKnown 之后的原因(AddKnown 内会先注册框架默认,业务侧后注册才能覆盖)。

KnownWasm/ · 纯前端宿主

只做引导,不放业务代码:

KnownWasm/
├── KnownWasm.csproj          Sdk=Microsoft.NET.Sdk.BlazorWebAssembly,只引 KnownApp
├── Program.cs                WebAssemblyHostBuilder + AddKnownApp + AddKnownClient
└── wwwroot/
    ├── index.html            加载占位 svg + blazor.webassembly.js + _content/Known/css|js
    └── ...                   静态资源

Program.cs 全文只有 5 行装配:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddKnownApp(AppType.Web);
builder.Services.AddKnownClient(o => o.BaseAddress = builder.HostEnvironment.BaseAddress);

await builder.Build().RunAsync();

KnownWeb/ · 全栈一体宿主

Sdk=Microsoft.NET.Sdk.Web,同时引用 KnownApp 与 KnownWasm(客户端就是被它托管的):

KnownWeb/
├── Program.cs                最小:AddApplication → Build → 一堆 Use → MapFallbackToFile
├── AppServer.cs              AddApplication / UseApplication / MapWebApis / CookieAuthProvider
├── IotLogHub.cs              (示例用)SignalR 中心:设备日志推送到 ConsoleLog 组件
├── IotLogWorker.cs           (示例用)后台 worker:模拟设备日志广播
├── appsettings.json          ConnString 等配置
├── wwwroot/                  服务端专属静态资源(服务端首屏 HTML 之外,客户端资源由 KnownWasm 出)
└── Logs/                     运行期文件日志(UseFileLogger)

AppServer.AddApplication 里做的三件事:AddKnownApp(AppType.Web) + AddKnownWeb(...) 挂数据库与文件日志 + Cookie 鉴权;UseApplication 里挂中间件、MapWebApis 扫端点、末尾 Services.UseDatabase(AppConfig.SeedAsync) 建表 + 灌种子。见 三种运行形态 的 Server 一节看完整代码。

KnownWin/ · 桌面宿主

Sdk=Microsoft.NET.Sdk.Razor + WinExe + UseWindowsForms,TFM net10.0-windows:

KnownWin/
├── Program.cs                手动 new ServiceCollection() + AddKnownApp(Desktop) + AddKnownDesktop + AddWindowsFormsBlazorWebView
├── MainForm.cs               BlazorWebView.Dock=Fill / HostPage="wwwroot/index.html" / RootComponents.Add<App>("#app")
├── KnownWin.csproj           WinExe / net10.0-windows / Costura.Fody IL 合并
├── FodyWeavers.xml           Costura 配置
└── wwwroot/index.html        几乎与 WASM 版逐字相同,只是引导脚本换成 blazor.webview.js

Program.cs 尾部还多一步 provider.UseDatabase(AppConfig.SeedAsync)——桌面没有 WebApplication,得显式在 ServiceProvider 上调用建库 + 种子。

一句话记住的分层

  • Known/ 只提供能力:组件、装配、契约、数据、鉴权。业务代码不在这里。
  • KnownApp/ 集中"我是谁 + 我要扫哪些程序集 + 我的菜单/顶栏长啥样":AddMyApp(AppType) 一行封装。
  • Known{Wasm|Web|Win}/ 只选运行形态:调用 AddMyApp,加一个"运行形态入口"(AddKnownClient / AddKnownWeb / AddKnownDesktop),加鉴权与部署相关的中间件;不重复装配元数据。

这条分层保证:同一份 KnownApp razor 可以塞进三种宿主,业务代码不需要为运行形态做任何改动。

下一步

看懂结构之后,去 写第一个 CRUD 页面——亲手在 KnownApp 里加一份 [Table] 实体、[Service] 实现、[Menu] 页面,看 AddKnownApp 是怎么把它们一次扫进路由与菜单的。

微信公众号二维码 扫码关注公众号
QQ 交流群二维码 扫码加入 QQ 群