跳至主要内容

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 / LiveCharts2NuGet 版本可用

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. 遷移步驟清單

  1. 建立新 SDK-style .csproj<Project Sdk="Microsoft.NET.Sdk">
  2. 設定 TargetFrameworknet8.0-windows(WPF/WinForms 加 -windows
  3. 遷移依賴<Reference><PackageReference>
  4. 處理 Native DLL<None ... CopyToOutputDirectory="PreserveNewest" />
  5. 設定 Nullable:初期 disable
  6. 更新 MVVM:手動 Property → [ObservableProperty]
  7. 導入 DIMicrosoft.Extensions.DependencyInjection
  8. 非同步化:同步阻塞 → async/await
  9. 測試 SDK 相容性:逐一驗證第三方 SDK
  10. 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 正常運作
.csproj631 行 → 38 行

文件版本:v1.0 | 建立日期:2026-03-16 | 對應 Issue:GST-173