尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【WPF】实战与部署全流程:从可维护架构到交付安装

【WPF】实战与部署全流程:从可维护架构到交付安装 【WPF】实战与部署全流程从可维护架构到交付安装开篇从 Demo 到产品中间隔着什么会写控件、会绑数据、会用 MVVM就能做出一个能跑的 Demo。但从「能跑的 Demo」到「能交付的产品」中间还隔着一大段路代码越堆越乱依赖纠缠不清配置硬编码在代码里换个环境要重新编译数据库连接、日志、异常处理到处乱写最后一步「怎么打包给用户装」更是一头雾水。这篇文章补上这段路前半部分讲如何搭建一个可维护的实战骨架后半部分讲如何把应用部署交付。第一部分实战——搭建可维护的应用骨架1. 项目结构规划一个规范的 WPF 项目按职责分层MyApp/ ├── MyApp.sln ├── src/ │ ├── MyApp.App/ # 启动项目View 入口 │ ├── MyApp.Core/ # 业务逻辑、实体、接口不依赖 UI │ └── MyApp.Infrastructure/ # 数据访问、外部服务实现 └── tests/ └── MyApp.Core.Tests/ # 单元测试核心原则Core 层不引用任何 UI 和框架细节这样业务逻辑才能被测试、被复用。2. 依赖注入DI用微软官方的Microsoft.Extensions.DependencyInjection管理依赖避免到处newApp.xaml.csusingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingSystem.Windows;publicpartialclassApp:Application{publicstaticIHostHost{get;privateset;}null!;protectedoverridevoidOnStartup(StartupEventArgse){base.OnStartup(e);HostMicrosoft.Extensions.Hosting.Host.CreateDefaultBuilder().ConfigureServices((context,services){// 注册服务services.AddSingletonMainWindow();services.AddSingletonMainViewModel();services.AddScopedIUserRepository,UserRepository();services.AddDbContextAppDbContext(oo.UseSqlite(Data Sourcemyapp.db));}).Build();varwindowHost.Services.GetRequiredServiceMainWindow();window.DataContextHost.Services.GetRequiredServiceMainViewModel();window.Show();}protectedoverridevoidOnExit(ExitEventArgse){Host.Dispose();base.OnExit(e);}}需要安装的 NuGet 包Microsoft.Extensions.Hosting Microsoft.Extensions.DependencyInjection3. 配置管理告别硬编码用appsettings.json管理连接字符串、API 地址等配置appsettings.json{ConnectionStrings:{Default:Data Sourcemyapp.db},AppSettings:{ApiBaseUrl:https://api.example.com,PageSize:50}}读取配置publicclassAppSettings{publicstringApiBaseUrl{get;set;}string.Empty;publicintPageSize{get;set;}20;}// 注册services.ConfigureAppSettings(context.Configuration.GetSection(AppSettings));// 注入使用publicclassMainViewModel{publicMainViewModel(IOptionsAppSettingsoptions){varsettingsoptions.Value;varbaseUrlsettings.ApiBaseUrl;}}CreateDefaultBuilder()默认就会加载appsettings.json记得把文件属性设为「复制到输出目录」。4. 数据库访问EF Core企业级应用常用 EF Core轻量场景可用 Dapper。这里以 EF Core SQLite 为例publicclassUser{publicintId{get;set;}publicstringName{get;set;}string.Empty;publicstringEmail{get;set;}string.Empty;}publicclassAppDbContext:DbContext{publicAppDbContext(DbContextOptionsAppDbContextoptions):base(options){}publicDbSetUserUsersSetUser();}在 ViewModel 里异步查询绝不阻塞 UI 线程publicclassMainViewModel{privatereadonlyIUserRepository_repo;publicMainViewModel(IUserRepositoryrepo)_reporepo;publicObservableCollectionUserUsers{get;}new();publicasyncTaskLoadAsync(){Users.Clear();varlistawait_repo.GetAllAsync();// 内部用 EF Core 异步查询foreach(varuinlist)Users.Add(u);}}5. 日志用 ILogger 而非 Console.WriteLineCreateDefaultBuilder已内置日志系统注入ILoggerT即可publicclassMainViewModel{privatereadonlyILoggerMainViewModel_logger;publicMainViewModel(ILoggerMainViewModellogger)_loggerlogger;publicasyncTaskLoadAsync(){_logger.LogInformation(开始加载用户数据);try{// ...}catch(Exceptionex){_logger.LogError(ex,加载用户数据失败);}}}生产环境可接入Serilog写文件、滚动归档、结构化日志。6. 全局异常处理不让应用「闪退」未捕获的异常默认会直接崩溃。在App里挂全局处理器publicApp(){DispatcherUnhandledExceptionOnDispatcherUnhandledException;AppDomain.CurrentDomain.UnhandledExceptionOnUnhandledException;TaskScheduler.UnobservedTaskExceptionOnUnobservedTaskException;}privatevoidOnDispatcherUnhandledException(objectsender,System.Windows.Threading.DispatcherUnhandledExceptionEventArgse){// 记录日志MessageBox.Show($发生错误{e.Exception.Message},错误,MessageBoxButton.OK,MessageBoxImage.Error);e.Handledtrue;// 标记已处理避免崩溃}全局异常处理是「兜底」不能替代业务里的try/catch。关键操作仍要局部处理。第二部分部署——把应用交付给用户1. 两种发布方式先搞清楚核心选择方式说明优点缺点框架依赖FDD只打包你的应用依赖系统安装 .NET 运行时体积小几 MB目标机器需装运行时自包含SCD把 .NET 运行时一起打包无需预装任何东西体积大几十 MB面向普通用户的桌面应用首选自包含——用户双击就能跑不用额外装运行时体验最好。2. dotnet publish 命令自包含发布win-x64dotnet publish-cRelease-rwin-x64 --self-containedtrue框架依赖发布dotnet publish-cRelease发布产物默认在bin\Release\net8.0-windows\win-x64\publish\目录。3. 单文件发布把整个应用含依赖打包成一个 .exe分发方便dotnet publish-cRelease-rwin-x64 --self-containedtrue\-p:PublishSingleFiletrue-p:IncludeNativeLibrariesForSelfExtracttrue单文件发布对 WPF 的支持已相当成熟注意首次启动会解压到临时目录略有延迟。若应用有被引用的额外 DLL 或资源需用-p:IncludeAllContentForSelfExtracttrue。4. ClickOnce自动更新利器ClickOnce 适合企业内部快速分发 自动更新发布后用户点一个链接就能安装且能自动检查更新。在 Visual Studio 里项目右键 → 发布 → 选择 ClickOnce → 配置发布位置和更新策略。底层也可以命令行操作但 VS 图形界面是最省事的方式。优点自动更新、无需管理员权限、一键安装。缺点默认装到用户目录不适合需要写系统目录的应用。5. MSIX现代化打包MSIX 是微软推荐的现代打包格式适合上架微软商店或企业分发在 VS 里给 WPF 项目添加「Windows 应用程序打包项目」MSIX。配置包名、版本、图标、证书。生成.msixbundle双击即可安装支持自动更新、干净卸载。优点干净卸载、权限沙箱、自动更新。缺点配置和签名有一定门槛证书过期要重新签名。6. 传统安装器Inno Setup需要完整控制安装流程写注册表、创建快捷方式、开机自启、选安装目录用Inno Setup免费或WiX最灵活。Inno Setup 脚本骨架[Setup] AppNameMyApp AppVersion1.0.0 DefaultDirName{autopf}\MyApp OutputDiroutput OutputBaseFilenameMyApp-Setup [Files] Source: publish\*; DestDir: {app}; Flags: recursesubdirs [Icons] Name: {autoprograms}\MyApp; Filename: {app}\MyApp.exe Name: {autodesktop}\MyApp; Filename: {app}\MyApp.exe [Run] Filename: {app}\MyApp.exe; Description: 立即运行; Flags: postinstall nowait流程先dotnet publish出自包含产物再用 Inno Setup 把publish目录打成安装包。7. 代码签名避免「未知发布者」警告未签名的安装包和 exeWindows 会弹出「Windows 已保护你的电脑」和 SmartScreen 警告劝退大量用户。解决方案购买代码签名证书OV/EV用signtool签名signtool sign /f certificate.pfx /p 密码 /tr http://timestamp.digicert.com /td sha256 MyApp.exeEV 证书可立即获得 SmartScreen 信誉OV 证书需要积累下载量。个人开发者至少要用 OV 证书签名否则分发体验很差。第三部分常见部署坑目标机器提示缺少 .NET 运行时改用自包含发布或引导用户装对应版本的 .NET Desktop Runtime。找不到 SQLite 或本地库自包含发布时确保原生库被包含或用IncludeNativeLibrariesForSelfExtract。配置/数据库文件路径不对不要写死路径用AppContext.BaseDirectory或%AppData%否则装到 Program Files 后无写权限。单文件首次启动慢属正常现象解压可用 ReadyToRun-p:PublishReadyToRuntrue改善启动速度。SmartScreen 报警代码签名 积累信誉别无捷径。32 位/64 位不匹配目标机器是 32 位 Windows 时发布用-r win-x86而不是win-x64。未做架构分层导致难维护DI、配置、日志、异常处理这些「地基」要在项目早期就搭好后期补成本极高。总结WPF 从开发到交付是一条完整的链路实战骨架项目分层 依赖注入 配置管理 EF Core 日志 全局异常这些是「可维护性」的地基。部署交付根据受众选择「自包含 / 框架依赖」用dotnet publish发布再按场景选 ClickOnce、MSIX 或 Inno Setup 打包。临门一脚代码签名决定用户能否顺利装上你的应用。一句话总结架构决定能不能长期维护打包决定能不能顺利交付签名决定用户敢不敢装。如果这篇文章对你有帮助欢迎点赞、收藏你在部署 WPF 应用时还踩过哪些坑或者想深入了解「CI/CD 自动构建发布」评论区见。本文命令基于 .NET 8/10 WPF兼容 .NET 6示例代码可运行。
返回列表