翻譯:使用 CoreWCF 升級 WCF 服務到 .NET 6
翻譯:使用 CoreWCF 升級 WCF 服務到 .NET 6
原文地址:https://devblogs.microsoft.com/dotnet/upgrading-a-wcf-service-to-dotnet-6/
大約在 3 年之前,我釋出過一篇將一個 WPF 應用遷移到 .NET Core 3 演練過程的部落格。這是一個被稱為 Bean Trader 的簡單商用交易示例程式。當時,我只能遷移示例解決方案的一部分。這個 Bean Trader 解決方案包括一個 WPF 客戶端應用和一個客戶端用來發布和接受交易的伺服器端應用。客戶端與伺服器端的通訊使用 WCF。因為 .NET Core 3 ( 以及後繼版本如 .NET 5 和 .NET 6 ) 支援客戶端的 WCF API,但是不支援伺服器端,我只能遷移 Bear Trader 的客戶端部分,而遺留下伺服器端繼續執行在 .NET Framework 上。
由於近期 CoreWCF 1.0 釋出了,我期待完成將 Bean Trader 升級到 .NET 6 上!
1. 關於 CoreWCF
CoreWCF 是一個社群驅動的專案,使得 WCF 可以執行在現代的 .NET 版本上。儘管 CoreWCF 不是微軟擁有的專案,微軟已經宣佈它將提供對 CoreWCF 的產品支援。對於新的開發工作推薦採用最新的技術,像 gRPC 和 ASP.NET WebAPI,但是對於需要遷移到 .NET 6 的現存的重度依賴於 WCF 技術的專案來說, CoreWCF 對於提供了巨大的幫助。
儘管 CoreWCF 支援眾多的 WCF 常見使用場景,它並不支援全部的 WCF 功能。你的遷移過程體驗非常依賴於你對 WCF 的使用與包含在 CoreWCF 中的功能的交集。如果你使用的功能還沒有包含在 CoreWCF 中,請在
2. 關於示例專案
示例專案 Bean Trader ( GitHub 地址 ) 是多年以前,我在演示如何遷移到 .NET Core 的時候建立的示例。因為該示例本來是僅僅用來展示如何遷移客戶端,Bean Trader 服務就非常簡單。它包含一個含有模型和介面的類庫,以及一個用來託管 WCF 服務的控制檯應用程式 ( 使用支援證書驗證的 Net.Tcp 傳輸 )。支援客戶端發出或者接受交易不同顏色的豆子。儘管該示例應用很小,我想它對於展示遷移到 .NET 6 的過程還是有用的。
3. 遷移
3.1 執行升級助理
為了使得遷移更快捷,我將使用 .NET 升級助理。升級助理是用來幫助使用者從 .NET Framework 升級到 .NET Standard 和 .NET 6 的命令列程式,使用互動方式。升級助理還不能自動從 WCF 遷移到 CoreWCF ( 儘管以及安排在計劃中 ),執行該工具仍然是有幫助的,因為專案檔案可以遷移,NuGet 包的引用可以被升級,以及其它處理等等。在該工具執行之後,我將手工完成從 WCF 遷移到 CoreWCF 的需要的變更處理。
為了安裝升級助理,我執行如下的 .NET SDK 命令:
dotnet tool install -g upgrade-assistant
升級助理安裝之後,我可以在 BeanTrader 解決方案上執行它來開始遷移過程。通過在解決方案 ( 而不是特定專案上 ) 執行,我可以通過執行一次該工具,來升級類庫和伺服器控制檯應用兩者。
升級解決方案的命令:
upgrade-assistant upgrade BeanTrader.sln
該工具隨即指導我進行一系列的升級步驟 ( 這會通過升級助理自動處理 ),如下所示:
我通過升級助理的完整步驟如下:
-
選擇入口點。這支援我選擇希望執行在 .NET 6 上的專案。基於該選擇,升級助理將決定要升級的專案以及升級的次序。我選擇
BeanTraderServer
-
選擇一個專案進行升級。這過渡到該工具開始升級指定專案。它建議我先升級
BeanTraderCommon
專案,然後升級beanTraderServer
專案更加合理,所以我選擇該升級順序。 -
備份
BeanTraderCommon
-
將
BeanTraderCommon
專案轉換為 SDK 風格的專案 -
升級
BeanTraderCommon
的 NuGet 包。該步驟替換對 System.ServiceModel 程式集的引用,使用 NuGet 包,例如 System.ServiceModel.NetTcp 來代替。 -
升級
BeanTraderCommon
的目標架構 ( TFM )。該工具建議使用 .NET Standard 2.0,因為該專案是一個純類庫專案,沒有任何 .NET 6 特定依賴。 -
此時,對輔助專案的升級已經完成,升級助理切換到升級
BeanTraderServer
-
備份
BeanTraderServer
-
轉換
BeanTraderServer
專案到 SDK 風格 -
將 System.ServiceMoel 引用替換為等價的 NuGet 包,如在
BeanTraderCommon
專案中一樣 -
升級
BeanTraderServer
專案的目標框架。工具建議為 .NET 6,因為該專案是控制檯應用 -
禁用不再支援的配置節。升級助理檢測到
BeanTraderServer
在它的 app.config 檔案中有一個system.ServiceModel
配置節,它不再被 .NET 6 所支援 ( 會導致執行時錯誤 ),所以它為我註釋掉了。最後,我們將會在其它檔案中重用這段註釋掉的配置節,來配置我們的 CoreWCF 服務。 -
在檢查 C# 源是否有任何必要的更改時,升級助手會發出有關 WCF 使用情況的警告。警告訊息提醒我,BeanTraderServer 使用伺服器端 WCF API,這些 API 在 .NET 6 上不受支援,並且該工具未進行升級。它告訴我,我需要手動進行更改,並建議升級到 CoreWCF、gRPC 或 ASP.NET Core。
-
清理升級。此刻,升級助理完成工作,所以它刪除一些臨時檔案並退出。
3.2 CoreWCF 遷移
現在升級助理已經完成了升級過程,是時候升級 CoreWCF 了。在 Visual Studio 中開啟 Bean Trader 解決方案,我發現 BeanTraderCommon 類庫可以成功構建。專案升級到 .NET Standard 已經完成了。而 BeanTraderServer
專案有一些錯誤,可以想到,關聯到不能找到某些 WCF 型別。
為了開始升級到 CoreWCF,我新增 CoreWCF.NetTcp NuGet 包的 1.0 版本引用。我還替換了 using System.ServiceModel;
,在 BeanTrader.cs 中匯入 using CoreWCF;
。除了在 program.cs 中我建立 ServiceHost 的錯誤之後,這解決了其它所有錯誤。
CoreWCF 構建於 ASP.NET Core 之上,所以我需要升級該專案來啟動一個 ASP.NET Core 宿主。BeanTrader 示例是一個自託管的服務專案,所以我只需要做一小點修改來設定一個 ASP.NET Core 宿主來執行我的服務,而不是直接使用 ServiceHost 完成。為了完成這一點,我將專案的 SDK 升級為 Microsoft.NET.Sdk.Web
( 因為專案使用了 ASP.NET Core ),將應用的 Main() 方法改為 async,並使用下面程式碼替換了對 ServiceHost 的設定。
存在不同型別的 WCF 專案 ( 不都是直接建立並執行 ServiceHost ),但所有的 CoreWCF 應用都是執行在 ASP.NET Core 的端點上。這裡展示的程式碼使用新的 .NET 6 的 minimal API 語法,來使用最少的程式碼啟動宿主並執行,它也可以微調為使用 ASP.NET Core 語法 ( 例如有獨立的 Startup.cs ),如果你願意的話,CoreWCF 示例中演示了這兩種方式。
注意證書的配置是從原來的示例專案中複製過來的,只用於演示目的。真實的場景下應該使用來自機器的證書儲存來使用證書,或者使用安全的位置,比如 Azure 的 Key Vault。另外,這也很好地演示了在使用 CoreWCF 的時候,宿主的屬性如何修改,但是設定伺服器證書是特別針對 NetTcp 場景的。對於 HTTPS 端點,SSL 通過 ASP.NET Core API 設定,與其它的 ASP.NET Core 應用一樣。
var builder = WebApplication.CreateBuilder();
// Add CoreWCF services to the ASP.NET Core app's DI container
builder.Services.AddServiceModelServices();
var app = builder.Build();
// Configure CoreWCF endpoints in the ASP.NET Core host
app.UseServiceModel(serviceBuilder =>
{
serviceBuilder.ConfigureServiceHostBase<BeanTrader>(beanTraderServiceHost =>
{
// This code is copied from the old ServiceHost setup and configures
// the local cert used for authentication.
// For demo purposes, this just loads the certificate from disk
// so that no one needs to install an untrusted self-signed cert
// or load from KeyVault (which would complicate the sample)
var certPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "BeanTrader.pfx");
beanTraderServiceHost.Credentials.ServiceCertificate.Certificate = new X509Certificate2(certPath, "password");
beanTraderServiceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
});
});
await app.StartAsync();
我還使用 await app.StopAsync()
替換了原來應用中的 host.Close()
呼叫。
3.3 升級配置資訊
如前所提及,.NET 6 預設沒有包含 system.serviceModel
配置節。但是眾多現存的 WCF 應用程式使用 app.config 和 web.config 來設定繫結配置。為了更容易遷移,CoreWCF 包含了可以從 xml 配置檔案中顯示載入配置資訊的 API。
為了使用 Bean Trader 伺服器的 WCF 配置,我從新增 CoreWCF.ConfigurationManager
NuGet 包開始。然後,我把原來應用的 app.config 配置檔案中的 system.serviceModel
配置節 ( 它被升級助理註釋掉了 ),複製到一個新的配置檔案中。該配置檔案可以使用任意名稱,不過我命名它為 wcf.config
。
在 WCF 與 CoreWCF 之間支援哪些 WCF 配置存在一些不同,所以我需要對 wcf.config
做一些如下的修改:
-
IMetadataExchange
還不被 CoreWCF 支援,所以刪除 mex 端點。我可以仍然使得 WSDL 可以被下載,記住,我隨後將展示如何做到這一點。 -
在服務模型配置中,元素
\<host>
不被支援。相反,端點的監聽埠在程式碼中配置。所以,我需要從 wcf.config 中刪除\<host>
元素,然後在應用的 Main() 方法中新增如下的程式碼行:builder.WebHost.UseNetTcp(8090);.
這應該在呼叫
builder.Build()
之前。
最後,我升級應用的 Main() 方法,新增配置到 ASP.NET Core 應用的依賴注入容器中。
builder.Services.AddServiceModelConfigurationManagerFile("wcf.config");
此時,應用將可以正常工作,客戶端也可以成功連線到它。我還希望使 WSDL 也可以使用,所以,我繼續對專案做一些修改。首先,新增如下程式碼到 Main() 方法中,來使得 ASP.NET Core 應用監聽到埠 8080 ( 因為以前的應用從該埠下載 WSDL ):
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(8080);
});
然後,當註冊服務的時候,我新增對 builder.Services.AddServiceModelMetadata()
的呼叫,來確保元資料服務可用,這樣我將獲得該 ServiceMetadataBehavior
物件例項,它以單例模式註冊,通過修改它來使得 WSDL 可以下載。這些程式碼需要在構建 app 之後,但是在啟動之前。
// Enable getting metadata/wsdl
var serviceMetadataBehavior = app.Services.GetRequiredService<ServiceMetadataBehavior>();
serviceMetadataBehavior.HttpGetEnabled = true;
serviceMetadataBehavior.HttpGetUrl = new Uri("http://localhost:8080/metadata");
通過這些修改,該 Bean Trader 服務現在完全遷移到了 .NET 6! 我可以執行該服務應用,並使用現在的客戶端連線到它。並且 WSDL 也可以通過 localhost:8080/metadata 來下載。想要看到完整的本文中所有的修改,你可以 檢視該 PR,它這樣修改了 Bean Trader 示例應用。最後,示例專案的 NetCore 資料夾中包含了只有 .NET Core 和麵向 .NET 6 的專案!
總結
Bean Trader 示例專案只是一個小專案,但是希望該演練過程展示了在 .NET 6 平臺上,使用 CoreWCF 來利用 WCF 服務繼續工作做需要的修改。除了引用不同的名稱空間之外,WCF 的服務實現幾乎不需要修改,多數的 xml 配置也得以重用。我做了使得服務宿主建立的修改 ( 現在服務通過 ASP.NET Core 託管 ),但是我仍然能夠重用以前用來定製服務宿主行為的程式碼。