1. 程式人生 > 其它 >翻譯:使用 CoreWCF 升級 WCF 服務到 .NET 6

翻譯:使用 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 中,請在

Feature Roadmap 中提供反饋,以便 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

該工具隨即指導我進行一系列的升級步驟 ( 這會通過升級助理自動處理 ),如下所示:

我通過升級助理的完整步驟如下:

  1. 選擇入口點。這支援我選擇希望執行在 .NET 6 上的專案。基於該選擇,升級助理將決定要升級的專案以及升級的次序。我選擇 BeanTraderServer

  2. 選擇一個專案進行升級。這過渡到該工具開始升級指定專案。它建議我先升級 BeanTraderCommon 專案,然後升級 beanTraderServer 專案更加合理,所以我選擇該升級順序。

  3. 備份 BeanTraderCommon

  4. BeanTraderCommon 專案轉換為 SDK 風格的專案

  5. 升級 BeanTraderCommon 的 NuGet 包。該步驟替換對 System.ServiceModel 程式集的引用,使用 NuGet 包,例如 System.ServiceModel.NetTcp 來代替。

  6. 升級 BeanTraderCommon 的目標架構 ( TFM )。該工具建議使用 .NET Standard 2.0,因為該專案是一個純類庫專案,沒有任何 .NET 6 特定依賴。

  7. 此時,對輔助專案的升級已經完成,升級助理切換到升級 BeanTraderServer

  8. 備份 BeanTraderServer

  9. 轉換 BeanTraderServer 專案到 SDK 風格

  10. 將 System.ServiceMoel 引用替換為等價的 NuGet 包,如在 BeanTraderCommon 專案中一樣

  11. 升級 BeanTraderServer 專案的目標框架。工具建議為 .NET 6,因為該專案是控制檯應用

  12. 禁用不再支援的配置節。升級助理檢測到 BeanTraderServer 在它的 app.config 檔案中有一個 system.ServiceModel 配置節,它不再被 .NET 6 所支援 ( 會導致執行時錯誤 ),所以它為我註釋掉了。最後,我們將會在其它檔案中重用這段註釋掉的配置節,來配置我們的 CoreWCF 服務。

  13. 在檢查 C# 源是否有任何必要的更改時,升級助手會發出有關 WCF 使用情況的警告。警告訊息提醒我,BeanTraderServer 使用伺服器端 WCF API,這些 API 在 .NET 6 上不受支援,並且該工具未進行升級。它告訴我,我需要手動進行更改,並建議升級到 CoreWCF、gRPC 或 ASP.NET Core。

  14. 清理升級。此刻,升級助理完成工作,所以它刪除一些臨時檔案並退出。

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 做一些如下的修改:

  1. IMetadataExchange 還不被 CoreWCF 支援,所以刪除 mex 端點。我可以仍然使得 WSDL 可以被下載,記住,我隨後將展示如何做到這一點。

  2. 在服務模型配置中,元素 \<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 託管 ),但是我仍然能夠重用以前用來定製服務宿主行為的程式碼。