将 .NET Framework 迁移到 .NET 之前该确认的事 - 动手前就决定成败的实战检查清单

· · .NET, .NET Framework, C#, 现代化, Windows 开发, 迁移

附中文工作表的 Excel 检查清单下载

.csprojTargetFramework 改成 net10.0,更新几个 NuGet 包,build 通过就结束。

……这种迁移相当理想化。实际上不是这样的情况更多。

.NET Framework 的现场有 System.Web、WCF、Web Forms、旧的 packages.configweb.config.install.xdt、原生 DLL、COM / ActiveX、只在设计时运作的第三方控件、隐含的 x86 前提、依赖设计工具的 ResX、旧的序列化器等,平时没意识到的前提相当隐蔽。

所以 .NET Framework 到 .NET 的迁移真正重要的是在实现之前的盘点。 如果动手前能拆解论点,迁移就不是「一场大赌注」而是「按顺序解决的作业」。

本文整理在把现有的 .NET Framework 4.x 业务应用程序 迁移到 当前的 .NET 之前,该确认什么。主要对象如下。

  • 类库
  • 控制台应用程序
  • Windows 服务
  • WinForms / WPF
  • ASP.NET Framework(MVC / Web API / Web Forms)
  • 使用 WCF 的应用程序
  • 使用 EF6 的应用程序

撰写时点为 2026-03-15。支持期间或官方工具的建议会变化,如果间隔较久之后阅读,请一并确认官方最新信息。

1. 先讲结论

先放不容易偏离的结论。

  • 在迁移前整理好 .NET Framework 一侧 是首要任务。Microsoft 的官方指南也推荐在迁移前把版本提升到 .NET Framework 4.7.2 以上,先完成 PackageReference 化、SDK 样式化、依赖关系更新。
  • 难度由 应用程序模型而非代码量 决定。类库或控制台相对轻松,ASP.NET Framework、Web Forms、WCF 服务端、WF 容易变重。
  • WinForms / WPF 即使迁移到 .NET 仍然是 Windows 专用。这里理解错了就会踩到「迁移后却装不上 Linux 容器」的经典坑。
  • ASP.NET Framework → ASP.NET Core 实质上是架构迁移。小规模应用程序有时能一口气迁完,但大型正式系统以分阶段迁移为前提更安全。
  • WCF 和 EF6 可以与运行时迁移分开处理。WCF 客户端有面向 .NET 的受支持包,EF6 则可以在迁到 modern .NET 之后再单独迁移到 EF Core。
  • 另一方面,AppDomain 创建、.NET Remoting、CAS、COM+、Workflow Foundation、BinaryFormatter 依赖 是红灯项目。不先发现,后期工时会爆炸。
  • packages.config / install.ps1 / XDT / content 资产 / 原生 DLL / COM / ActiveX / x86 前提 即使 build 通过,运行时或设计时也容易掉坑,动手前要盘查清楚。
  • 2026-03 时点 .NET 10 是 LTS。新迁移的落地点基本以 现行 LTS 为基准来考虑是自然的。
  • 没准备测试、度量、回滚方案的迁移很危险。迁移与其说是实现作业,更是把前提条件一个一个可视化的作业。

2. 先决定「现在是否真的该迁移」

最先该做的不是思考「迁移的方法」。 是决定 这个应用程序现在真的该迁移吗

这里含糊不清,容易变成技术上正确但业务上过重的迁移,或者相反明明该迁移却拖得太久的判断失误。

2.1 留在 .NET Framework 的判断也是可能的选项

.NET Framework 4.8.1 只要运行在受支持的 Windows 上,就会持续得到支持。也就是说,并非「不立即全部迁到 modern .NET 就有风险」这么简单的话题

但留下有明确的限制。

  • 出不了 Windows 专用的圈子
  • 持续背负 ASP.NET Web Forms 或旧服务端技术栈
  • 难以享受新版 .NET 的性能改善、语言特性、生态系统的好处
  • 容易与云、容器、CI/CD 的现代前提脱节

相反,如果强烈依赖以下内容,暂时依靠 .NET Framework 4.8.1 稳定运维,另行规划替换 是合理的。

  • Web Forms 的画面资产量大
  • 需要严格保持 WCF 服务端兼容
  • Workflow Foundation 或 COM+ 依赖较深
  • 第三方设计时组件不支持 modern .NET
  • 业务上不允许大规模规格变更

2.2 各选项会改变什么

选项 会变好什么 留下什么 / 失去什么 适合的情况
留在 .NET Framework 4.8.1 不破坏现有资产、容易稳定运维 Windows 专用、旧应用程序模型、现代化的极限 有较强的 legacy 依赖,现阶段以业务优先想稳定运维
迁移到 modern .NET 但留在 Windows 能现代化运行时或工具链。性能、开发体验、SDK 样式的好处很大 Windows API 依赖仍然存在。不会变跨平台 WinForms / WPF、Windows Service、使用 Windows API 的业务应用程序
迁移到 modern .NET,并考虑将来上 Linux / 容器 / 云 提高部署位置的自由度。运维模型也容易更新 需要先剥离 Windows 专用 API 或 app model 想把服务端向云靠拢、也想改革基础设施

重要的不是 想不想迁移,而是先决定 迁移之后想落地在哪里

3. 该先决定的 4 个方针

3.1 落地点的 .NET 版本

撰写时点,按 Microsoft 的支持策略 .NET 10 是 LTS。 另一方面 .NET 8 LTS.NET 9 STS 都预定在 2026-11-10 结束支持。

所以,今后要新从 .NET Framework 迁移的话,如果没有特殊理由,以现行 LTS 为落地点 是自然的选择。

这里实务上的想法很简单。

  • 想短期结束的小型迁移 直接落地到现行 LTS
  • 以长期运维为前提的核心系统 也基本以现行 LTS 来考虑
  • 「因为现有库的情况想用前一个 LTS」作为情况是可以存在的,但要 用日期看到底能维护到什么时候 再做判断

3.2 继续 Windows 专用,还是将来瞄准跨平台

这个判断,该看的论点会相当不同。

  • 继续 Windows 专用 的话,可以走用 WPF / WinForms 或 Windows Compatibility Pack,先把运行时现代化的现实路线。
  • 也想瞄准将来 Linux / 容器化 的话,需要在早期阶段盘点 System.Drawing.Common、注册表、WMI、EventLog、Windows Service、COM、Office Interop 等依赖 Windows 前提的 API。

不先决定这个就开始迁移,中途会出现「固定在 Windows 上就行了吗」「不对,本来是想装进容器的」这样的话题反复扭曲。

3.3 一次性完成还是分阶段迁移

迁移的形式大致有 3 种。

  • 接近 in-place 的一次性迁移
  • 新旧并行的 side-by-side 迁移
  • 以路由 / 库为单位一点点靠拢的分阶段迁移

特别是 ASP.NET Framework 应用程序,Microsoft 的指南也明确介绍了 incremental migration(增量迁移)。 在不想停止正式环境、功能数量多、周边依赖多的条件下,从一开始就以分阶段迁移为前提设计会比较顺畅。

3.4 决定什么 「不放入本次迁移对象」

迁移容易失败的原因是堆积了太多要做的事。

例如同时做以下事项容易变得沉重。

  • .NET Framework → .NET
  • ASP.NET Framework → ASP.NET Core
  • EF6 → EF Core
  • Windows 服务端 → Linux 容器
  • 身份验证基础设施变更
  • 日志 / 监控基础设施变更
  • 数据库的迁移

当然全部迟早都可能是必要的。 但 是否需要同时做 是另一个问题。

现实中如下分开会更容易成功。

  1. 先现代化运行时和项目结构
  2. 在此基础上迁移 app model
  3. 最后更新 ORM、身份验证、云、监控

4. 动手前该准备好的基础

Microsoft 的迁移前指南相当实用。 简言之,在迁移前先让现在的 .NET Framework 项目靠近现代化的入口

4.1 提升到 .NET Framework 4.7.2 以上

官方指南推荐迁移前以 .NET Framework 4.7.2 以上 为目标。 理由是 .NET Standard 即使没有直接保留现有 API,也更容易靠向更新的替代 API。

实务上,如果可能的话以 4.8.1 为基准来考虑会更易懂。

  • 从支持角度看很自然
  • 作为 .NET Framework 一侧的最终稳定点便于处理
  • 容易建立「先在现行 Framework 一侧整理好」的方针

先做这个会改变什么

  • .NET Standard 2.0 共享库的处理更容易稳定
  • 能先减少旧运行时带来的干扰
  • 容易区分兼容性问题的原因是「Framework 太旧」还是「modern .NET 化」

4.2 转向 PackageReference

迁移前指南推荐把引用转向 PackageReference 形式。 先做这个,依赖关系管理会透明许多。

PackageReference 化会改变什么

  • 包引用集中到 csproj
  • 传递依赖更容易看清
  • restore 的行为与 modern .NET 一侧保持一致
  • 与 CLI / CI 的配合度好

但这里有坑。

典型的坑

NuGet 的官方文档明确写出从 packages.configPackageReference 的迁移有以下限制。

  • Visual Studio 内置迁移在 ASP.NET 项目中不可用
  • 依赖 install.ps1 / uninstall.ps1 的包可能不会按预期运作
  • content 文件夹的资产有时会被忽略
  • XDT 转换(例如 web.config.install.xdt)不会被应用
  • lib 目录直下的程序集结构较旧的包可能无法很好地解析

也就是说,只是切换包格式 这种想法是不可取的。 特别是 classic ASP.NET 在安装 NuGet 包时有改写 web.config 的传统,迁移时隐藏的前提容易暴露出来。

4.3 转向 SDK 样式

迁移前指南也推荐转换为 SDK 样式的项目格式

这相当有效。

SDK 样式化会改变什么

  • csproj 大幅简洁
  • PackageReference 配合度好
  • 容易实现 multi-targeting(多目标)
  • 容易转向以 dotnet build / dotnet test / dotnet publish 为中心的 CI/CD
  • 更接近 modern .NET 一侧的结构,后半段的差异会减少

反之,带着旧 csproj 和旧 NuGet 管理方式直接跳到 modern .NET,差异会太大

4.4 先更新依赖关系

同样按照官方指南,依赖关系尽量靠向 可用的最新版本,如果可能就靠向 支持 .NET Standard 的版本

先做这个的意义

  • 能提前知道「这个包能在 modern .NET 上用吗」
  • 防止旧依赖关系成为干扰
  • 容易把 shared library 做成 netstandard2.0
  • 容易让后段迁移作业聚焦在「代码迁移」本身

4.5 也要确认官方工具的前提

2026-03 时点,Microsoft 的引导重心已经转移到 GitHub Copilot 现代化 一侧。 也就是说,比起只以过去的迁移辅助工具为前提,把它看作包含评估、规划、代码修正、验证的一整套支持流程更符合实务。

但现行文档的前提是 Visual Studio 2026 或仍受支持的 Visual Studio 2022 系列GitHub CopilotC# 代码

需要确认这一点的理由

  • 对官方工具能期待什么会随之改变
  • 能对齐团队的 IDE / build agent / 扩展的前提
  • 能判断 在 VB.NET 解决方案中不要对自动化期待过高

VB.NET 混杂在项目中的现场并不罕见。 所以「最新官方工具能帮到什么程度」在最初就确认清楚很有价值。

5. 按项目类别估算难度

迁移常被以「从 .NET Framework 到 .NET」一概而论,但现实中 各项目类别是不同的游戏

5.1 大致的难度感

类别 难度感 主要论点
类库 低~中 API 兼容性、依赖关系、目标拆分
控制台 / 批处理 / 部分 Windows Service 低~中 发布方式、原生依赖、配置
WinForms / WPF 继续 Windows 专用、设计工具、第三方 UI、BinaryFormatter 周边
ASP.NET MVC / Web API 中~高 到 ASP.NET Core 的 app model 迁移、身份验证、session、配置、DI
ASP.NET Web Forms 画面模型差异大、以替换 UI 层为前提
WCF 客户端 包替换、契约、配置
WCF 服务端 CoreWCF 还是 gRPC / HTTP API 重新设计
EF6 → EF Core 同时实施 ORM 不同、行为差异、迁移历史

5.2 类库的「共享边界」怎么切是关键

类库相对容易迁移。 但那仅限于 库真正做到库式分离的时候

有以下依赖时难度就会上升。

  • 接触了 System.Web
  • 直接引用 HttpContext.Current
  • 公开 API 中含有 WPF / WinForms 的类型
  • 过度依赖注册表、WMI、EventLog 等 Windows API
  • 依赖 AppDomain 或 Remoting

看作 只切出业务逻辑就轻,带有 app model 就重 会更易懂。

5.3 WinForms / WPF 容易迁移但继续是 Windows 专用

WinForms 和 WPF 能迁移到 .NET。 但两者都 继续是 Windows 专用框架

这里理解错期望值会很危险。

  • 会变好的
    • 能用上 modern .NET 的运行时、语言、SDK 样式
    • 容易转向现代的 CI/CD 或包管理
    • 容易获得部分性能、可维护性改善
  • 不变的
    • 仍然是 Windows 专用
    • UI 控件或设计时组件的兼容性问题仍在
    • ActiveX / COM / 原生 DLL 问题不会消失

另外,WinForms / WPF 有必要确认 BinaryFormatter 的影响 的情况。 特别是 clipboard、drag & drop、ResX、设计时的序列化涉及自定义类型时,把 target 提升到 .NET 9 以后容易表面化。

5.4 ASP.NET Framework 不是「运行时迁移」而是「app model 迁移」

从 ASP.NET Framework 到 ASP.NET Core 的迁移,Microsoft 指南也明确说是 non-trivial(不简单)。 这不仅是 API 名字变了,而是 前提架构不同

容易出现差异的地方如下。

  • Hosting model(托管模型)
  • Middleware pipeline(中间件管道)
  • Request processing model(请求处理模型)
  • Session / Cache
  • Authentication / Authorization(身份验证 / 授权)
  • Configuration(配置)
  • Dependency Injection(依赖注入)
  • Logging / Monitoring(日志 / 监控)

也就是说,ASP.NET Framework 应用程序该确认的如下。

  • 从哪个路由 / endpoint 先迁移
  • 能否从 shared library 中剥离 System.Web 依赖
  • 身份验证 / session / 异常处理 / 日志如何对齐
  • 是否以不停止正式环境为前提分阶段迁移

特别是大型应用程序,一开始就以 incremental migration 为前提来考虑会更现实。

5.5 Web Forms 不是「资产迁移」而是从「职责分解」开始

Web Forms 与 ASP.NET Core 不是同一个 app model。 所以在估算上 不要以能直接搬走画面资产为前提 会更安全。

实务上常从以下分解开始。

  • 分离画面逻辑和业务逻辑
  • 分解埋在 Page / UserControl / ViewState 中的职责
  • 把业务逻辑或数据访问逃逸到 shared library
  • 用 Razor Pages / MVC / Blazor 等其他模型重新组织 UI

也就是说 Web Forms 项目 在运行时迁移之前,是否有职责分解计划 很重要。

5.6 WCF 客户端和 WCF 服务端要分开考虑

这里不要混在一起会比较好。

WCF 客户端

WCF Client 有面向 modern .NET 的 受支持 NuGet 包。 所以 如果只是调用 WCF 的一侧,可能没有外观上看起来那么重 的情况是存在的。

WCF 服务端

另一方面,hosting(托管) WCF 服务的一侧是另一回事。 Microsoft 指南大致介绍了以下 2 条现代化路径。

  • CoreWCF 保持现有客户端兼容的方向
  • 转向 gRPC 等现代 RPC / HTTP 基础的方向

但 CoreWCF 不是把 WCF 全部原样搬来,只是子集。 也就是说保持现有客户端兼容是适合的,但 前提是代码变更和测试

6. 盘查 .NET 中不能用 / 直接用会卡住的技术

这是动手前绝对该做的部分。 Microsoft 有一份 在 .NET Framework 能用但 .NET 6+ 不能用的技术 一览表。

6.1 容易变成红灯的技术

技术 在 .NET 中的状态 该如何考虑
AppDomain.CreateDomain 等 AppDomain 的创建 不支持 用独立进程 / 容器 / AssemblyLoadContext 来分离
.NET Remoting 不支持 重新设计为 IPC、HTTP、gRPC、Socket、Pipe 等
CAS / Security Transparency 作为安全边界不支持 用 OS / 容器 / 权限分离来考虑
System.EnterpriseServices(COM+) 不支持 分离、替换以 COM+ 为前提的设计
Workflow Foundation 不支持 包括 CoreWF 等替代方案,另行估算
WCF server 内置不再直接支持 选 CoreWCF 或 gRPC
BinaryFormatter .NET 9 以后实现总是抛出异常 序列化器迁移、ResX / clipboard / drag & drop 审查

6.2 AppDomain「即使部分 API 还留着,创建是另一个问题」

AppDomain 周边略微复杂。 .NET 虽然部分 API surface 还留着,但 创建新 AppDomain 进行隔离 的用法不受支持。

所以以下用途使用 AppDomain 的情况需要重新设计。

  • 插件隔离
  • 动态加载后的卸载
  • 部分信任代码的隔离
  • 临时运行环境的分离

迁移前该看的不是 是否出现 AppDomain 这个词,而是 为什么使用 AppDomain

6.3 Remoting「比外观更深」

Remoting 当然不用说,delegate 的 BeginInvoke() / EndInvoke() 调用 这类源自 Remoting 的行为也可能进入影响范围。

所以搜索时也看以下内容会更安全。

  • System.Runtime.Remoting
  • MarshalByRefObject
  • RealProxy
  • BeginInvoke( / EndInvoke(

6.4 BinaryFormatter 会在目标版本上突然浮出水面

BinaryFormatter 代码库越旧就越常见「无意识地在使用」。

  • 持久化数据
  • 缓存
  • Session 存储
  • 插件状态
  • clipboard / drag & drop
  • ResX
  • WinForms / WPF 设计工具周边

在 .NET 9 以后,BinaryFormatter 不再包含在运行时的实现中,API 总是会抛出 PlatformNotSupportedException。 也就是说这不是「以后再想」,而是 决定目标版本时就要先审查的论点

6.5 先用来 grep 的搜索词

动手前对整个解决方案用以下关键词搜索一遍,视野会相当不同。

System.Web
HttpContext.Current
System.Runtime.Remoting
MarshalByRefObject
AppDomain
BinaryFormatter
ServiceHost
ChannelFactory
System.EnterpriseServices
Workflow
packages.config
web.config.install.xdt
install.ps1
DllImport
AxInterop
Microsoft.Office.Interop

找到其中 1 个并不代表立即出局。 而是 了解哪里能走标准路线迁移、哪里需要另开一条路的地图

7. 决定 Windows 专用前提允许到什么程度

迁移中常见的误解是「迁到 .NET 就变跨平台了」。 没有这种魔法。应用程序如果深度绑定 Windows,迁移后一般仍是 Windows 专用。

7.1 保持 Windows 专用的迁移很现实

Microsoft 有 Windows Compatibility Pack,提供让注册表、WMI、EventLog、Windows Service、Directory Services 等许多 Windows 系 API 能在 modern .NET 中使用的手段。

这很重要。

  • 先想迁到 modern .NET
  • 暂时不走出 Windows
  • 所以 暂时允许 Windows API 依赖

这样的现场相当有效。

也就是说迁移的初期目标不一定是 跨平台化

7.2 但 Windows 专用 API 也是「以后才会显现的债」

有了 Windows Compatibility Pack 不代表什么都可以放心。

  • 想部署到 Linux 容器
  • 以 Kubernetes 为前提
  • 想让 macOS / Linux 开发者也能跑同一套 build
  • 将来想在云中减少 Windows VM

有这类目标的话,现在就先把 Windows API 依赖可视化 会更好。

7.3 System.Drawing.Common 特别容易被误解

System.Drawing.Common 在 .NET 6 以后是 Windows 专用库。 也就是说如果有涉及图像处理或文字绘制的代码,先决定以下。

  • 要继续在 Windows 上运维吗
  • 将来也想在 Linux / macOS 上运行吗

前者的话暂时保持也是可以的选择。 后者的话需要从迁移计划一开始就包含替换为 SkiaSharp 或 ImageSharp 等方案。

7.4 典型的 Windows 绑定气味

以下引用或 API 出现时,以「至少一开始按 Windows 专用迁移」为前提来估算会更安全。

  • Microsoft.Win32.Registry
  • System.Management
  • System.Diagnostics.EventLog
  • System.ServiceProcess
  • System.DirectoryServices
  • System.Drawing
  • DllImport / P/Invoke
  • COM 引用
  • AxInterop.*
  • Microsoft.Office.Interop.*

8. 共享库的切分方式决定难度

在较大的解决方案中,迁移的成败由 shared library 的切分方式决定 并不夸张。

8.1 先分类

库大致分 3 类比较容易整理。

  1. 纯业务逻辑 / 领域逻辑
  2. 稍微依赖 app model 的中间层
  3. 紧密耦合 UI / Web / Windows API 的层

其中最先该迁移的是第 1 类。

  • 计算
  • 规则判定
  • DTO / 契约
  • 领域服务
  • 简单的数据转换

如果能把这些干净地拿出来,难度会一下子降低。

8.2 netstandard2.0 现在仍然是有效的桥梁

按照 Microsoft 的指南,需要与 .NET Framework 一侧共存的 shared library,首先考虑 .NET Standard 2.0 是基本做法。

这里重要的有 2 点。

  • .NET Framework 不支持 .NET Standard 2.1
  • 想从新旧两侧都引用 shared library,2.0 是现实解

8.3 netstandard2.0 化会改变什么

方针 如何改变 适合的情况 注意点
netstandard2.0 容易被新旧两侧引用 纯业务逻辑、通用契约、工具类 不能承载 app model 特有的 API
multi-target(例如 net48;net10.0 保持通用代码同时持有环境差异 有一定环境差异的库 条件分支或 build 管理会增加
一步到位专用化为 net10.0 将来最干净 不需要新旧共存的新层 不能从 .NET Framework 引用

8.4 兼容模式并非万能

.NET Standard 2.0 有引用 .NET Framework 库的兼容模式。 但这 不是什么都能透明运作的魔法

例如像 WPF 这类以 app model 特有 API 为前提的库通常很困难。 也就是说,shared library 也只应该限制在真正能共享的职责范围内 这一点很重要。

8.5 ASP.NET 系库能否剥离 System.Web 是胜负关键

如果要分阶段迁移 ASP.NET Framework,shared library 如果直接依赖 HttpContext.CurrentSystem.Web 会相当痛苦。

此时基本策略是以下之一。

  • System.Web 依赖推到接口之外
  • 把来自 HttpContext 的信息改成用 DTO 接收
  • 在迁移过渡期使用 adapter(适配器)
  • 实在不行就用 multi-target 分阶段剥离

8.6 库按 leaf-first(叶子优先)提升

ASP.NET 的 incremental migration 指南明确指出以 postorder depth-first(后序深度优先),也就是 从叶子节点开始 提升支撑库。

这不限于 Web,一般的解决方案中同样相当有效。

  • 依赖目标先提升,上层的展望更清晰
  • 容易将兼容性问题局部化
  • 容易以库为单位进行测试

9. 盘点 NuGet / 外部依赖 / 第三方组件

这里如果处理得粗糙,迁移后半段会看到最痛苦的场面。

9.1 依赖关系分 4 种比较容易整理

  1. 公开 NuGet 包
  2. 公司内部 private package / internal library
  3. 本地 DLL 引用
  4. COM / ActiveX / 原生 DLL / SDK

只看第 1 类是不够的。 真正危险的是第 3 和第 4 类。

9.2 各依赖该确认的内容

对各依赖至少要看以下内容。

  • 是否以 modern .NET 为目标
  • 是否支持 PackageReference
  • SDK 样式是否没问题
  • 是否有 x86 / x64 / ARM64 的限制
  • 是否依赖设计时工具或 Visual Studio 扩展
  • 是否以 install script / config transform 为前提
  • 支持是否会持续下去

9.3 第三方 UI / 报表 / 设计时组件另行估算

WinForms / WPF / ASP.NET 的迁移,这里相当有影响。

  • 表格控件
  • 报表引擎
  • PDF 输出组件
  • 图表组件
  • 设计工具集成型的 UI 库
  • ActiveX 封装

这些涉及 不只运行时,还包含设计时支持。 迁移估算只看「能否编译」通常会失准。

9.4 原生 DLL 和位数一定要看

在 .NET Framework 时代看起来以 AnyCPU 运作,实际上可能依赖以下内容。

  • 固定为 x86 的 COM
  • 32bit 专用 ActiveX
  • 特定版本的 VC++ Runtime
  • 已签名的原生 DLL

这一带不是 modern .NET 化之后才突然冒出来的问题,而是 原本就存在的限制表面化了。 所以迁移前把它可视化有价值。

10. 把 EF6、序列化器、数据周边当作独立问题处理

运行时的迁移和数据访问或序列化的重新设计,尽量分开当作独立问题处理 会更顺利。

10.1 EF6 → EF Core 不是直接升级

Microsoft 的 EF 指南也说 EF Core 是 EF6 的 total rewrite(完全重写),没有 direct upgrade path(直接升级路径)

所以使用 EF6 的应用程序按以下顺序处理更现实。

  1. 先迁到 modern .NET
  2. 必要的话保留 EF6 让应用程序继续运行
  3. 之后作为另一个独立项目迁移到 EF Core

这一点相当重要。 不要把 runtime migration 和 ORM migration 一起做,难度会明显下降。

10.2 保留 EF6 会改变什么

  • 好处
    • 可以延后数据访问层的差异
    • 能聚焦在 business logic 或 app model 的迁移上
    • 不会混入「EF Core 的行为差异」
  • 注意点
    • 从新开发角度看 EF Core 是本命
    • EF6 Designer / EDMX 的使用形态有另外的限制

10.3 基于 EDMX 的 EF6 要看「设计时」

EF6 的文档中写着 EF Designer 在 .NET / .NET Standard 项目或 SDK 样式的 .NET Framework 项目上不直接支持

也就是说基于 EDMX 的应用程序需要分开考虑。

  • 运行时是否能正常运作
  • 是否能使用 Designer
  • 生成的代码如何处理

大量使用 EDMX 的话,这一点要从一开始就纳入估算会更安全。

10.4 BinaryFormatter 或自制序列化容易成为「隐藏依赖」

序列化器只用代码搜索容易漏看。

  • 持久化格式
  • 消息
  • 缓存
  • 旧 WCF / SOAP 契约
  • ResX
  • 剪贴板 / drag & drop

这一带也涉及 数据的兼容性。 也就是说不只要确认「能否 build」,还需要确认 能否读取旧数据

11. 把配置、发布、运维、CI/CD 也纳入迁移对象

迁移的对象不只是源代码。

11.1 配置文件

.NET Framework 一侧在 app.config / web.config 中可能承载了相当多内容。

  • connection string(连接字符串)
  • custom config section(自定义配置节)
  • WCF endpoint 配置
  • binding redirect(绑定重定向)
  • diagnostics(诊断)
  • ASP.NET 各种配置
  • 包安装时 transform 的结果

modern .NET 一侧的配置持有方式或读取路径可能会变。 所以「配置文件放到最后处理」很危险。

最初要做的是 配置的盘点

  • 配置文件里有什么
  • 哪些是应用程序启动时必须的
  • 哪些是环境差异
  • 哪些是由 NuGet 或 installer 自动注入的

11.2 发布方式

以下内容也该确认。

  • 是在 IIS 下运行吗
  • 是 Windows Service 吗
  • 是 Scheduled Task 吗
  • 是 ClickOnce / MSI / 自制 installer 吗
  • 是以本地服务器为前提吗
  • self-contained / framework-dependent 哪个更合适

执行本体能迁移,但如果 发布和启动的机制仍是旧前提,最后会卡住。

11.3 日志、监控、运维流程

运维周边也容易被漏看。

  • 是以 Windows Event Log 为前提吗
  • 是否查看 Performance Counter
  • 是否以 WMI 为基础进行监控
  • 服务账户或权限是否固定
  • 日志输出位置是否以本地文件为前提

迁移后代码能跑,但 运维流程转不动 的情况很常见。

11.4 CI/CD 和 build agent

迁移前也要确认以下内容。

  • build agent 能否安装必要的 .NET SDK
  • nuget.exe / msbuild.exe 为前提的 pipeline 如何处理
  • 是否转向以 dotnet CLI 为基础
  • 测试执行、coverage、publish 的 job 如何更新
  • 公司内部模板或 reusable pipeline 是否以旧格式为前提

本地环境能跑但 CI 挂了 是迁移中常见的现象。

12. 现实的迁移推进方式

根据到目前为止的论点,现实的推进方式大致落在下面的形式。

12.1 先整理好现行 Framework 一侧

  1. 靠到 .NET Framework 4.7.2 以上,可能的话到 4.8.1
  2. 提升依赖关系
  3. 重新检查 packages.config
  4. 尽可能靠到 PackageReference 和 SDK 样式
  5. 确认现有应用程序在这个状态下能正常运作

光做这个,后段的差异就会明显减少。

12.2 先救出 shared library

接着把业务逻辑或通用契约靠到 netstandard2.0 或 multi-target。 提升的顺序基本是 leaf-first(叶子优先)

12.3 应用程序本体按 app model 变换策略

  • 类库 / 控制台 / 部分服务 相对容易直接推进
  • WinForms / WPF 保持 Windows 专用来现代化
  • ASP.NET MVC / Web API 规模小就一次性完成,规模大就分阶段迁移
  • Web Forms 以替换画面资产为前提,先把 shared logic 剥离出来
  • WCF 服务端 先决定是维持 CoreWCF 还是重新设计为 gRPC

12.4 坚持「不要一次做完所有事」

特别想避免的组合如下。

  • runtime 迁移 + ORM 全部替换
  • runtime 迁移 + 身份验证基础设施变更
  • runtime 迁移 + 云全面迁移
  • runtime 迁移 + 监控基础设施变更
  • runtime 迁移 + UI 框架革新

即使全部都是必要的,不要堆到同一个 sprint 里 大多数情况会更成功。

12.5 先建立测试和基线再动手

至少想要具备以下条件。

  • 单元测试
  • 主要业务流程的集成测试
  • 代表性画面 / API 的快照式验证
  • 性能基线
  • 主要日志的确认方法
  • 回滚流程

在无法确定「迁移后哪里坏了」的状态下推进相当危险。

13. 动手前检查清单

以能直接粘贴到项目管理工具的形式列出。

13.1 方针

  • 能用一句话说明为什么要迁移
  • 决定本次落地点是 Windows 专用的 modern .NET 还是 将来的跨平台化
  • 决定目标的 .NET 版本
  • 决定本次范围 不包含的内容(EF Core 化、身份验证革新、云全面迁移等)

13.2 现行 .NET Framework 一侧的整理

  • 靠到 .NET Framework 4.7.2 以上,可能的话到 4.8.1
  • 把依赖关系提升到较新的版本
  • 盘查 packages.config 的使用情况
  • 确认 PackageReference 化的可行性
  • 确认 SDK 样式化的可行性
  • 现有应用程序在该状态下能 build、启动、测试

13.3 应用程序类别和技术选型

  • 按类库 / 桌面 / Web / WCF 等区分难度
  • 理解 WinForms / WPF 保持 Windows 专用
  • 理解 ASP.NET Framework 是到 ASP.NET Core 的 app model 迁移
  • 把 Web Forms 的 UI 层替换纳入估算
  • WCF 按客户端和服务端分别评估

13.4 不支持的技术、要注意的 API

  • 盘查 AppDomain 依赖
  • 盘查 Remoting / MarshalByRefObject / BeginInvoke / EndInvoke
  • 盘查 CAS / Security Transparency / COM+ / WF
  • 盘查 BinaryFormatter 依赖
  • 盘查 System.Web 依赖

13.5 Windows 专用依赖

  • 盘查注册表、WMI、EventLog、Windows Service、Directory Services 的使用情况
  • 盘查 System.Drawing.Common 的使用情况
  • 盘查 COM / ActiveX / Office Interop / P/Invoke / 原生 DLL
  • 确认 x86 / x64 / ARM64 的限制

13.6 shared library 和数据访问

  • 把 shared library 分类为 业务逻辑 / app model 紧密耦合层
  • 盘查能 netstandard2.0 化的部分
  • 盘查需要 multi-target 的库
  • 判断能否保留 EF6 只先迁移 runtime
  • 确认 EDMX / Designer 依赖

13.7 运维和 build

  • 盘点配置文件
  • 确认发布方式(IIS / Service / MSI / ClickOnce 等)
  • 确认日志 / 监控 / 权限 / 执行账户前提
  • 确认 CI/CD 和 build agent 是否需要更新
  • 制定回滚流程

14. 总结

.NET Framework 到 .NET 的迁移重要的是,比起「用哪个命令迁移」,更重要的是 在动手前看透什么能直接走、什么是另一个问题

特别想掌握的如下。

  • 在迁移前整理好 .NET Framework 一侧
  • 按应用程序模型区分难度
  • 先盘查不能用的技术
  • 决定 Windows 专用前提允许到什么程度
  • 决定如何切分 shared library
  • 不要同时堆积太多 ORM、身份验证、云迁移

迁移中,最初 1 周的估算精度会有很大不同。 反过来说,如果那 1 周能把论点整理清楚,后半段就能相当顺利地转入普通开发节奏。

「先改成 net10.0 试试看」作为探索并不坏。 但作为正式迁移,在那之前有该看的地方。本文整理的正是这些内容。

15. 参考资料

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

常见问题

汇总了咨询这一主题时常见的问题。

.NET Framework 迁移到 .NET 之前该先做什么?
先在迁移前整理好 .NET Framework 一侧。官方指南推荐把版本提升到 .NET Framework 4.7.2 以上(实务上以 4.8.1 为基准更易懂),并先完成 PackageReference 化、SDK 样式化与依赖关系更新。带着旧 csproj 和旧 NuGet 管理方式直接跳到 modern .NET,差异会太大。迁移真正重要的是在实现之前的盘点,把前提条件一个一个可视化。
WinForms / WPF 迁移到 .NET 之后就能跨平台了吗?
不能。WinForms 和 WPF 即使迁移到 .NET 仍然是 Windows 专用框架,这里如果理解错了就会踩到「迁移后却装不上 Linux 容器」的经典坑。会变好的是能用上 modern .NET 的运行时、语言、SDK 样式与现代 CI/CD;不变的是 Windows 专用、UI 控件或设计时组件的兼容性问题,以及 ActiveX / COM / 原生 DLL 的问题。另外也需要确认 BinaryFormatter 的影响。
哪些依赖是 .NET 迁移的红灯项目?
AppDomain 创建、.NET Remoting、CAS(Code Access Security)、COM+、Workflow Foundation、BinaryFormatter 依赖是红灯项目,不先发现的话后期工时会爆炸。另外 packages.config、install.ps1、XDT 转换、content 资产、原生 DLL、COM / ActiveX、隐含的 x86 前提,即使 build 通过,运行时或设计时也容易出问题,动手前要盘查清楚。
迁移的落地版本该选哪个 .NET?
文章撰写时点(2026-03).NET 10 是 LTS,而 .NET 8 LTS 和 .NET 9 STS 都预定在 2026-11-10 结束支持,所以新的迁移如果没有特殊原因,以现行 LTS 为落地点最自然。想短期结束的小型迁移和长期运维的核心系统基本都以现行 LTS 来考虑,若因既有库的情况想用前一个 LTS,要用日期确认它会维护到什么时候再判断。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表