- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
迁移到 ElectronNET.Core 通常可以走 Migration Guide 的常规路径,但当你需要自定义 ASP.NET 端口、使用定制化的 ElectronHostHook 代码,或在多项目解决方案中组织包依赖时,会遇到常规步骤覆盖不到的边界场景。本文以 Advanced-Migration-Topics.md 为主线,结合仓库源码(StartupManager.cs、ElectronNetRuntime.cs、RuntimeControllerAspNetBase.cs 等)逐项拆解这些高级场景的配置原理与实操方案,帮助你完成一次无痛、可控的迁移。
一、为什么会出现"高级迁移主题"?
ElectronNET.Core 相比旧版 Electron.NET 有三大结构性变化,直接导致旧式配置方式失效:
- ASP.NET-first 启动模式成为主流:.NET 进程先启动、Electron 作为子进程被拉起。这意味着 ASP.NET 端口的分配时机变得动态且依赖运行时状态,静态写在清单文件里的端口不再可靠。
electron.manifest.json被废弃:配置全面迁移到 MSBuild 元数据(AssemblyMetadataAttribute)和 Visual Studio 项目设计器,构建信息随程序集元数据注入。- TypeScript 工具链升级:自定义 ElectronHostHook 需要跟随新版 TypeScript 编译器、Node.js 类型定义和 ESLint 规则重新对齐。
不理解这些变化,迁移后很容易踩到"端口连不上""ElectronHostHook 编译失败""类库项目里找不到 API"等坑。本文逐个解决。
二、自定义 ASP.NET 端口配置
2.1 旧方案为什么被移除?
在旧版 Electron.NET 中,开发者可以在electron.manifest.json里指定 WebPort。但在 ElectronNET.Core 的 ASP.NET-first 架构下,Electron 是后启动的,它需要等到 ASP.NET 服务真正就绪、拿到实际绑定的端口后才能建立 socket 桥接。此时端口是一个运行时动态值,依赖启动方式、打包形态和 Kestrel 的绑定策略,预先写死在清单文件里会造成时序依赖和端口不一致问题。
从源码可以看出,新架构下端口信息完全通过程序集元数据与运行时探测两条路径获得:
- 构建期:MSBuild 把
AspNetHttpPort写入AssemblyMetadataAttribute; - 运行期:ASP.NET 宿主通过
IServerAddressesFeature读取 Kestrel 实际监听的地址端口,并回填ElectronNetRuntime.AspNetWebPort。
2.2 新方案:MSBuild 元数据配置
在项目文件(.csproj)中添加如下 ItemGroup 即可固定 ASP.NET 端口:
<ItemGroup> <AssemblyMetadata Include="AspNetHttpPort" Value="4000" /> </ItemGroup>这段配置的底层效果可以在 StartupManager.cs 中看到:GatherBuildInfo()会从入口程序集读取所有AssemblyMetadataAttribute,其中Key == "AspNetHttpPort"的项会被解析为整数并写入ElectronNetRuntime.AspNetWebPort:
var httpPort = attributes.FirstOrDefault(e => e.Key == "AspNetHttpPort")?.Value; // ... if (httpPort?.Length > 0 && int.TryParse(httpPort, out var port)) { ElectronNetRuntime.AspNetWebPort = port; }2.3 端口在运行期如何被使用与回填?
ElectronNetRuntime.AspNetWebPort是int?类型(见 ElectronNetRuntime.cs),它贯穿三条关键链路:
链路一:Kestrel 绑定。在 WebHostBuilderExtensions.cs 中,UseElectronCore会读取ElectronNetRuntime.AspNetWebPort ?? 0作为 Web 端口:
- 若元数据未配置(值为 0),则采用动态端口分配,此时 Kestrel 必须绑定
127.0.0.1而非localhost(源码注释明确说明:端口为 0 时绑定 localhost 会失败); - 打包启动模式下,内容根目录会被指向进程基目录,保证静态资源随应用二进制一起分发。
链路二:运行时回填。在 RuntimeControllerAspNetBase.cs 的HandleReady()中,当 SocketBridge、Electron 进程和 ASP.NET 生命周期全部就绪后,会从IServerAddressesFeature取出首个监听地址,用uri.Port覆盖AspNetWebPort。这保证了即便使用动态端口,最终拿到的也是真实端口。
链路三:窗口加载 URL。在 WindowManager.cs 中,如果加载地址是裸的http://localhost,会自动拼接上ElectronNetRuntime.AspNetWebPort。开发者也可以手动使用该属性拼接任意路径:
await browserWindow.WebContents .LoadURLAsync($"http://localhost:{ElectronNetRuntime.AspNetWebPort}/mypage.html");仓库中的示例控制器也大量使用这一模式,例如 WindowsController.cs 和 CrashHangController.cs。
实践建议:
- 固定端口(如 4000)适合需要稳定 URL 的场景(外部集成、调试固定入口);
- 不配置
AspNetHttpPort时系统会自动分配端口,适合多实例并行开发,最终端口以ElectronNetRuntime.AspNetWebPort为准; - 不要把端口写死在前端静态资源里,应始终通过运行时属性读取。
三、自定义 ElectronHostHook 配置
3.1 适用范围判断
[!NOTE] 本节改动仅在使用自定义 ElectronHostHook 实现时才需要! 如果项目里有一个
ElectronHostHook文件夹,但你并未修改其中的代码,也没有使用它的演示功能(Excel 和 ZIP),可以直接把该文件夹从项目中删除,无需任何配置。
仓库中的 ElectronNET.WebApp/ElectronHostHook 与 ElectronNET.Samples.ElectronHostHook/ElectronHostHook 都是标准的 ElectronHostHook 示例结构,包含connector.ts、index.ts、package.json与tsconfig.json,可以作为你自定义实现的参考基线。
3.2 更新 package.json:Node.js 类型与 TypeScript 最低版本
新版工具链对依赖版本有最低要求,以下是相关改动的最小版本清单:
{ "devDependencies": { "@types/node": "^22.18", "typescript": "^5.9.3" }, "dependencies": { "socket.io": "^4.8.1", } }各依赖的用途:
| 依赖 | 最低版本 | 作用 |
|---|---|---|
@types/node | ^22.18 | 与 Node.js 22.x API 类型定义对齐,保证process、fs、path等 API 的类型提示正确 |
typescript | ^5.9.3 | 最新语言特性与更强的类型检查 |
socket.io | ^4.8.1 | Electron 主进程与 .NET 侧 socket 桥接通信所需(见 SocketIOConnection.cs) |
需要注意的是,示例项目(如 ElectronNET.WebApp/ElectronHostHook/package.json)中的tsconfig.json采用commonjs模块、ES2019目标并排除node_modules,这与下方项目文件中的TypeScriptModuleKind设置保持了一致。
3.3 更新项目文件:接入最新 TypeScript 编译器
以下改动会让 ASP.NET 项目直接使用最新的 TypeScript 编译器(通过 MSBuild 集成):
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.3" /> <PropertyGroup> <TypeScriptModuleKind>commonjs</TypeScriptModuleKind> <TypeScriptUseNodeJS>true</TypeScriptUseNodeJS> <TypeScriptTSConfig>ElectronHostHook/tsconfig.json</TypeScriptTSConfig> </PropertyGroup> <ItemGroup> <Compile Remove="publish\**" /> <Content Remove="publish\**" /> <EmbeddedResource Remove="publish\**" /> <None Remove="publish\**" /> <TypeScriptCompile Remove="**\node_modules\**" /> </ItemGroup>逐项说明:
Microsoft.TypeScript.MSBuild 5.9.3:把 TypeScript 编译纳入 Visual Studio 构建流程,替代以往依赖独立 npm 脚本或全局 tsc 的做法;TypeScriptModuleKind = commonjs:与 Electron 主进程的 CommonJS 模块体系匹配,避免 ESM/CJS 混用导致的require报错;TypeScriptUseNodeJS = true:使用本机 Node.js 执行编译,保证与@types/node版本一致;TypeScriptTSConfig:指向你的ElectronHostHook/tsconfig.json,让 MSBuild 复用项目内已有的编译配置;publish\**排除项:防止发布目录被当成源码重复编译;node_modules排除项:避免第三方 JS 被 TypeScript 编译器误当作源文件处理。
3.4 集成收益
完成上述配置后可以获得:
- 现代 TypeScript:最新的语言特性与更严格的类型检查,减少运行时类型错误;
- 更新的 Node.js 类型:兼容 Node.js 22.x API(如新的
fs、path类型签名); - ESLint 集成:与仓库中 eslint.config.js 一致的代码质量与风格约束;
- MSBuild 编译:与 Visual Studio 构建流程深度集成,构建、清理、发布全链路统一,不再依赖手工 npm 命令。
迁移指南中也强调了这一点:Migration Guide 指出新版工具链"消除了此前影响自定义 ElectronHostHook 实现的兼容性问题"(详见 What's New 的 TypeScript Integration 章节)。
四、多项目解决方案的排错与组织
在多项目解决方案中使用 ElectronNET.Core 时,包引用的边界必须清晰,否则会出现"类库项目里Electron.WindowManager不可用"或"启动项目重复初始化运行时"之类的症状。
4.1 包引用的正确分布
| 项目类型 | 应安装的包 | 说明 |
|---|---|---|
| 类库项目 | ElectronNET.Core.Api | 只需要 API 定义(Electron静态入口、Entities 类型等),不引入运行时逻辑 |
| 启动项目(可执行/ASP.NET 项目) | ElectronNET.Core+ElectronNET.Core.AspNet(ASP.NET 场景) | 运行时、构建逻辑与 ASP.NET 集成只在启动项目里存在 |
规则背后的原因:
ElectronNET.Core包含构建逻辑与项目系统集成(MSBuild 目标、Electron 分发),应只存在于启动项目中,避免多项目重复执行构建任务;ElectronNET.Core.AspNet提供UseElectron()扩展(WebApplicationBuilderExtensions.cs)与运行时控制器,仅适用于承载 Web 宿主的启动项目;- 类库项目若想调用 Electron API,引用
ElectronNET.Core.Api即可,通过项目引用或共享文件把配置统一起来。
注意:
ElectronNET.Core.Api会被ElectronNET.Core自动带出(NuGet 依赖传递)。在 ASP.NET 项目里直接dotnet add package ElectronNET.Core即可,无需显式添加 API 包。包结构详见 Package Description。
4.2 常见的多项目排错清单
- API 类型缺失(编译错误):确认类库项目引用了
ElectronNET.Core.Api; - 运行时重复初始化(重复创建窗口/重复启动 Electron):确认只有启动项目引用
ElectronNET.Core与ElectronNET.Core.AspNet; - 配置不一致:通过共享的项目引用或共享文件(如公共的
.props)统一AspNetHttpPort、RuntimeIdentifier等 MSBuild 属性,避免各项目各自为政; - 端口冲突:多启动项目同时运行时,给每个启动项目配置不同的
AspNetHttpPort,或用动态端口分配避免硬编码冲突。
五、迁移后的验证清单
完成高级配置后,建议按以下顺序验证:
- 构建验证:
dotnet build无编译错误,确认ElectronHostHook的 TypeScript 已通过 MSBuild 编译产出 JS; - 端口验证:启动后检查控制台输出或直接访问
http://localhost:{AspNetWebPort},确认端口符合预期;动态端口场景下用ElectronNetRuntime.AspNetWebPort打印实际值; - 窗口验证:Electron 窗口正确加载 ASP.NET 页面,说明 socket 桥接与端口协商成功;
- 打包验证:参考 Package Building 验证打包启动模式(packaged)下内容根目录切换后静态资源仍可访问;
- 跨平台验证:若目标平台包含 Linux,参考 Debugging 切换
RuntimeIdentifier与 WSL 调试配置再验证一遍。
六、下一步
- Migration Guide:完整的常规迁移流程(包更新、
electron.manifest.json迁移、启动代码改造); - What's New?:ElectronNET.Core 全部新特性总览;
- Getting Started / ASP.NET Core Setup:新项目的开发工作流与端口用法;
- Startup Methods:八种启动场景与启动方式选择;
- System Requirements:Node.js 22.x 与 .NET 8.0+ 等前置条件。
- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
相关推荐
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考