☰
从零搭建灌装监控系统(二十一):项目打包与部署,完结篇
2026/10/18 23:10:29 网站建设 项目流程

项目打包与部署(完结篇)

这是「从零搭建灌装监控系统」系列第21篇,也是最后一篇。功能写完了,测试过了,但把整个目录拷到现场机器上双击,它不一定跑得起来。这篇把系统打成真正能交付的包:发布配置、自包含运行时、单文件的取舍,以及首次启动必须自动完成的初始化。


拷过去打不开

第一次交付的时候,我把开发机上bin\Debug整个目录拷到 U 盘,拿到现场,双击 exe。

弹了一个框:“需要安装 .NET Desktop Runtime”。

现场的工控机装的是 Win7 升级上来的 Win10,没有 .NET 8 桌面运行时。现场也没网,装不了。我只好回办公室,重新发布一次。

第二次我发了自包含版本,能跑了。但第三天操作员说软件打开要等十几秒,比在我这儿慢得多——单文件解压的开销。

第三次交付前,我把该做的都做了一遍。这篇就是那份清单。


发布命令

先明确一件事:bin\Debug不是部署包。它包含调试符号、没有优化、依赖开发机上的运行时。部署要用publish:

dotnet publish FillTrack.csproj ^-cRelease ^-rwin-x64 ^ --self-containedtrue^-p:PublishSingleFile=true ^-o.\publish

参数逐个说:

参数作用能不能省
-c Release关掉调试符号、打开优化不能省
-r win-x64指定目标运行时(RID)不能省
--self-contained true把 .NET 运行时打进包里看现场
-p:PublishSingleFile=true合成单个 exe可省,有取舍
-o .\publish输出目录可省

自包含:现场机器不一定有运行时

这是第一个必须做的决定。

框架依赖发布(--self-contained false)产出的包很小,几十 KB 到几 MB,但要求目标机器装了对应版本的 .NET 桌面运行时。工业现场的机器通常:

  • 没网,装不了运行时
  • 有网也不敢随便装——很多厂区禁止私自安装软件
  • 系统版本杂,Win10 各种小版本都有

自包含发布把运行时一起打包,产物大概 150-200 MB。换来的是:双击就能跑,不依赖机器上装了什么。

对现场交付来说,这个交换非常值。U 盘拷贝本来就无所谓体积,但“打不开”是现场事故。


单文件:方便,但有代价

PublishSingleFile=true把几十个 dll 合成一个 exe,看起来干净很多。

代价是首次启动变慢。单文件 exe 运行时需要把内容解压到临时目录,冷启动可能多出好几秒。第二次启动会快一些,因为解压结果被缓存了——但缓存在哪个目录、会不会被清理工具删掉,都不完全可控。

所以在项目里加了这么一个设置:

<PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net8.0-windows</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings><UseWPF>true</UseWPF><IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract><Platforms>AnyCPU;x64</Platforms></PropertyGroup>

IncludeAllContentForSelfExtract让内容文件也参与自解压,避免运行时要访问的配置/资源文件散落在 exe 外面找不到。

取舍建议:如果现场对启动速度敏感(比如操作员上岗就开机、开机就要用),用非单文件的自包含发布——一个文件夹,多几十个文件,但启动快、路径行为跟开发时一致。单文件的收益主要是“看起来整洁”和“不容易误删某个 dll”。


不要开裁剪

看到 200 MB 的包,第一反应是开PublishTrimmed=true瘦身。

WPF 项目不要开裁剪。

WPF 大量依赖反射:XAML 里的{Binding}、DataTemplate按类型名匹配、依赖属性的注册,都发生在运行时。裁剪器只做静态分析,看不到这些隐式引用,会把“看起来没用到”的类型删掉。结果是编译通过、发布成功、运行时报TypeLoadException或绑定静默失效。

更隐蔽的是绑定失效不报错。界面能打开,只是某些字段永远是空的——这种 bug 在现场极难定位。

同理,ReadyToRun之类的优化可以按需开,但裁剪对 WPF 是明确的风险项。


运行时的目录约定

打包只是第一步,程序跑起来之后往哪儿写数据同样要想清楚。

不要把数据库和日志写在 exe 所在目录。如果部署到C:\Program Files\下,普通用户没有写权限,程序一启动就崩。

用用户数据目录:

vardataRoot=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"FillTrack");Directory.CreateDirectory(Path.Combine(dataRoot,"Data"));Directory.CreateDirectory(Path.Combine(dataRoot,"Logs"));vardbPath=Path.Combine(dataRoot,"Data","filltrack.db");DbProvider.Initialize($"Data Source={dbPath}");

三个目录各司其职:

目录内容是否随程序升级保留
Data\SQLite 数据库✅ 必须保留
Logs\滚动日志文件按保留策略清理
配置设备参数 JSON✅ 必须保留

现场升级软件的时候,通常只替换程序文件,数据目录不动。所以数据目录必须在程序目录之外——否则升级时整个覆盖,历史生产记录全没了。


首次启动要自动做完的事

一个能交付的程序,第一次运行应该自己把环境准备好,而不是让人看一份安装文档。启动流程按这个顺序:

否

是

启动

创建数据目录和日志目录

初始化日志 Serilog

初始化数据库 DbProvider

AutoSyncStructure 建表

有用户吗?

创建默认管理员账号

跳过

加载配置 无则用安全默认值

初始化设备服务

弹出登录窗口

进入主界面

顺序不能乱,尤其两点:

日志初始化必须在数据库之前。否则数据库初始化失败时,错误没地方记,现场只能看到程序闪退。

建表和建默认账户在登录之前。登录要查用户表,表不存在直接崩。


部署清单

每次交付前对照一遍:

  • dotnet publish -c Release发布通过
  • 自包含发布,目标机器无需装 .NET 运行时
  • 未开启PublishTrimmed
  • 数据库/日志/配置写在用户数据目录,不在程序目录
  • 首次启动能自动建表、建默认账户、建目录
  • 默认账户密码已在交付前修改或强制首登修改
  • 数据目录不在升级覆盖范围内
  • 现场机器上实际双击验证过一次
  • 断网状态下验证过启动和离线功能

最后两条最容易被跳过,也最容易出问题。在开发机上跑通不等于在现场机器上跑通。


踩坑记录

拷bin\Debug当部署包

带调试符号、没优化、依赖开发机环境。必须走publish。

框架依赖发布到没装运行时的机器

现场弹“需要安装 .NET Desktop Runtime”。工业现场大概率装不了,用自包含。

对 WPF 开裁剪

编译通过、发布成功、运行时绑定静默失效。这种 bug 在现场几乎没法查。不要开。

数据和程序放同一个目录

部署到Program Files下没写权限;升级覆盖时历史数据一起丢。数据目录必须在程序目录之外。

日志初始化放在数据库之后

数据库初始化失败时没有任何记录,现场只能看到闪退。


本篇小结

环节要点
发布命令dotnet publish -c Release -r win-x64 --self-contained
运行时自包含,现场不用装 .NET
单文件方便但冷启动慢,按现场需求取舍
裁剪WPF 不要开
数据目录用户数据目录,与程序目录分离
首次启动建目录 → 初始化日志 → 建库建表 → 建默认账户 → 登录
验证必须在现场机器上双击跑一次

系列完结

二十一篇,从一行代码都没有,到一个能拷到现场、双击就跑的灌装监控系统。回头看这条路径:

打地基— MVVM 骨架、依赖注入、导航、主题
接通设备— Modbus RTU 协议、串口/TCP 双模式、轮询循环、断线重连
留住数据— SQLite + FreeSql、条码追溯、CSV 导出、配置原子写入
让它说话— 报警生命周期、四路日志、日志查看器
给人看— Dashboard、实时曲线、自定义控件
能交付— 单元测试、权限管理、打包部署

如果只能留三条经验给准备做类似项目的人:

第一,先想清楚"出错时怎么办"。断线要重连、配置要能恢复、导出要能取消、密码要能挡住。这些逻辑不写,软件在办公室永远看着正常,一到现场就出问题。这个系列里我花在异常路径上的篇幅,比花在正常路径上多。

第二,别让界面直接碰数据。从第一层的 MVVM 到最后一层的打包,所有设计都指向同一个目标:让"谁负责什么"有明确边界。ViewModel 不碰 Modbus,控件不碰 Service,测试不碰现场数据。边界一清楚,改一处就不会崩一片。

第三,测试不是可选项。手动验证二十次和自动验证二十次,看着结果一样,区别在第二十一次——那时候你还能不能确定没改坏。

这个系列记录的是一个单机灌装监控上位机,通信协议是 Modbus RTU,规模不大。如果你对更复杂的工业控制场景感兴趣——多设备并行、工艺配方编排、安全联锁、权限分级更细、部署到产线集群——可以看我的另一个系列「从零搭建工业控制系统」,那边把这些问题都展开写过。

写到这里就结束了。感谢一路看到这里的读者,如果这个系列帮你在自己的项目里少踩一个坑,那它就值了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询