冠军

导航

翻译:使用 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 托管 ),但是我仍然能够重用以前用来定制服务宿主行为的代码。

posted on 2022-05-09 11:38  冠军  阅读(1646)  评论(5编辑  收藏  举报