Legacy 遷移指南(.NET Framework → .NET 8)
本文件根據 InspectionHost 的實際遷移經驗(.NET Framework 4.8 → .NET 8), 整理遷移路徑、注意事項與常見問題。
1. 適用專案
| 專案 | 現有版本 | 遷移狀態 |
|---|---|---|
| Leyu.InspectionHost | .NET 4.8 → .NET 8 | ✅ 已完成(LEY-59) |
| Walton Line1/2/3 | .NET 4.5 / VS 2015 | 未規劃 |
| ENR_DUC | .NET 4.8 | 未規劃 |
2. 遷移前評估清單
2.1 第三方 SDK 相容性
| 狀態 | SDK | 說明 |
|---|---|---|
| ✅ | Delta DIASECS(netcoreapp3.1) | 完全相容 .NET 8 |
| ⚠️ | KGS Common(.NET 4.5) | 需測試 .NET 8 相容性 |
| ⚠️ | Adlink Motion(Native P/Invoke) | 需確認 x64 DLL |
| ✅ | CommunityToolkit.Mvvm | 原生支援 .NET 8 |
| ✅ | HandyControl / LiveCharts2 | NuGet 版本可用 |
2.2 UI Framework
| 從 | 到 | 複雜度 |
|---|---|---|
| WinForms → WPF | 需重寫 UI(高) | 建議新專案直接用 WPF |
| WinForms → WinForms(.NET 8) | 僅更新專案格式(低) | 保留現有 UI |
3. .csproj 格式轉換
3.1 Before:傳統格式(631 行)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<LangVersion>7.3</LangVersion>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<!-- 每個 Configuration|Platform 一個 PropertyGroup -->
<!-- 每個檔案手動列出 <Compile>, <EmbeddedResource>, <None> -->
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, ...">
<HintPath>..\Dll\Newtonsoft.Json.dll</HintPath>
</Reference>
</Project>
3.2 After:SDK-style 格式(38 行)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.*" />
<PackageReference Include="HandyControl" Version="3.5.1" />
</ItemGroup>
</Project>
3.3 關鍵差異
| 面向 | .NET Framework | .NET 8 SDK-style |
|---|---|---|
| 檔案列舉 | 手動 <Compile> 每個檔案 | 自動 globbing(*.cs) |
| 依賴 | <Reference> + HintPath | <PackageReference> NuGet |
| 平台設定 | 每個 Config|Platform 獨立 | 單一 <TargetFramework> |
| 輸出路徑 | 手動 <OutputPath> | 自動(bin/Release/net8.0-windows/) |
4. 依賴管理遷移
4.1 NuGet 套件升級
| 舊版(DLL 參考) | 新版(NuGet) |
|---|---|
Newtonsoft.Json 6.0.0 (local DLL) | <PackageReference Include="Newtonsoft.Json" Version="13.*" /> |
M2Mqtt.Net 4.3.0 (local DLL) | 評估替代套件(MQTTnet) |
System.Data.SQLite (local DLL) | <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.*" /> |
4.2 Native DLL 處理
非受控 DLL(Unmanaged)維持 <None> + CopyToOutputDirectory:
<!-- 受控 DLL 參考 -->
<ItemGroup>
<Reference Include="DIASECS">
<HintPath>..\..\lib\DIASECS.dll</HintPath>
</Reference>
</ItemGroup>
<!-- 非受控 Native DLL(不用 Reference) -->
<ItemGroup>
<None Include="..\..\lib\hasp_net_core.dll" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\..\lib\hasp_windows_x64_24160.dll" CopyToOutputDirectory="PreserveNewest" />
<!-- 保留子目錄結構 -->
<None Include="..\..\lib\cpsrt\win\x64\cpsrt.dll"
CopyToOutputDirectory="PreserveNewest"
Link="cpsrt\win\x64\cpsrt.dll" />
</ItemGroup>
Link 屬性保留 Native DLL 的子目錄結構。
5. MVVM 現代化
5.1 Before:手動 INotifyPropertyChanged
public class ViewModel : INotifyPropertyChanged
{
private string _status;
public string Status
{
get => _status;
set
{
if (_status != value)
{
_status = value;
OnPropertyChanged(nameof(Status));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
5.2 After:Source Generator
public partial class ViewModel : ObservableObject
{
[ObservableProperty]
private string _status;
// 自動產生 Status property + PropertyChanged 通知
}
程式碼量減少 ~70%。
6. DI 整合
6.1 Before:無 DI(手動建立或 ServiceLocator)
var service = new SecsService(); // 直接 new
6.2 After:Microsoft.Extensions.DependencyInjection
var services = new ServiceCollection();
// Mock/Real 切換
if (useRealSecs)
services.AddSingleton<ISecsHostService, SecsHostService>();
else
services.AddSingleton<ISecsHostService, MockSecsHostService>();
services.AddSingleton<MainViewModel>();
var provider = services.BuildServiceProvider();
var mainWindow = provider.GetRequiredService<MainWindow>();
mainWindow.DataContext = provider.GetRequiredService<MainViewModel>();
7. Nullable Reference Types
遷移初期建議關閉,避免大量警告:
<Nullable>disable</Nullable>
後續逐步啟用並修復。
8. 非同步模式
8.1 Before:同步阻塞
public void Connect()
{
_driver.Init();
_driver.Start(); // 阻塞 UI Thread
}
8.2 After:Task-based Async
public Task ConnectAsync(string eqIp, string eqPort)
{
return Task.Run(() =>
{
_driver.Init();
_driver.Start();
});
}
9. 遷移步驟清單
- 建立新 SDK-style .csproj(
<Project Sdk="Microsoft.NET.Sdk">) - 設定 TargetFramework:
net8.0-windows(WPF/WinForms 加-windows) - 遷移依賴:
<Reference>→<PackageReference> - 處理 Native DLL:
<None ... CopyToOutputDirectory="PreserveNewest" /> - 設定 Nullable:初期
disable - 更新 MVVM:手動 Property →
[ObservableProperty] - 導入 DI:
Microsoft.Extensions.DependencyInjection - 非同步化:同步阻塞 →
async/await - 測試 SDK 相容性:逐一驗證第三方 SDK
- Build 驗證:
dotnet build+dotnet run
10. InspectionHost 遷移經驗
| 項目 | 結果 |
|---|---|
| Delta DIASECS SDK | ✅ netcoreapp3.1 DLL 完全相容 .NET 8 |
| HASP / CodeMeter Licensing | ✅ Native DLL 透過 <None> + Link 正常運作 |
| WinForms → WPF | ✅ 全面重寫(搭配 HandyControl Dark Theme) |
| DI 導入 | ✅ Mock/Real SECS 雙模式切換 |
| CommunityToolkit.Mvvm | ✅ Source Generator 正常運作 |
| .csproj | 631 行 → 38 行 |
文件版本:v1.0 | 建立日期:2026-03-16 | 對應 Issue:GST-173