翻译:使用 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 的过程还是有用的。

翻译:使用 CoreWCF 升级 WCF 服务到 .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 

该工具随即指导我进行一系列的升级步骤 ( 这会通过升级助理自动处理 ),如下所示:

翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6

我通过升级助理的完整步骤如下:

  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。
    翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6

  14. 清理升级。此刻,升级助理完成工作,所以它删除一些临时文件并退出。

3.2 CoreWCF 迁移

现在升级助理已经完成了升级过程,是时候升级 CoreWCF 了。在 Visual Studio 中打开 Bean Trader 解决方案,我发现 BeanTraderCommon 类库可以成功构建。项目升级到 .NET Standard 已经完成了。而 BeanTraderServer 项目有一些错误,可以想到,关联到不能找到某些 WCF 类型。

翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6

为了开始升级到 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 的项目!

翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6

总结

Bean Trader 示例项目只是一个小项目,但是希望该演练过程展示了在 .NET 6 平台上,使用 CoreWCF 来利用 WCF 服务继续工作做需要的修改。除了引用不同的命名空间之外,WCF 的服务实现几乎不需要修改,多数的 xml 配置也得以重用。我做了使得服务宿主创建的修改 ( 现在服务通过 ASP.NET Core 托管 ),但是我仍然能够重用以前用来定制服务宿主行为的代码。

发表评论

相关文章