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

翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6的更多相关文章

  1. 关于已部署的WCF服务升级的问题

    在日常的开发过程中,我们会经常迭代发布不同的版本,所以WCF服务的接口也会经常处于变动的状态,比如在传递实体类中新加一个字段.修改参数名称等等关于服务升级的问题.但是我们不可能让已发布的版本重新引用新 ...

  2. 如何使用花生壳 发布WCF服务 进行外网访问

    当我们发布WCF服务的时候,可以直接通过服务器的域名或者IP进行. 但是如果仅仅是通过花生壳进行域名解析,需要我们自己在设置的时候注意以下几点, 直接用图说明问题 1.首先配置花生壳,在红色处填写一个 ...

  3. IIS发布WCF服务项目之本地

    最近由于项目需求,要做一个上传文件附件的功能,由于是多用户访问,所以这就用到了WCF服务,程序编写完成就需要发布了, 下面记录下发布到IIS的过程: 1,安装IIS 第一步:检查Windows7中II ...

  4. Azure开发者任务之七:在Azure托管服务中托管WCF服务角色

    在一个托管服务中托管一个WCF服务角色和托管一个ASP.Net Web Role基本类似. 在上一篇文章中,我们学习了如何使用WCF Service Web Role. 在本文中,我会对上一篇文章进行 ...

  5. 创建WCF服务寄宿到IIS

    一.WCF简介: Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台. 整合了原有的win ...

  6. IIS8中部署WCF服务出错:HTTP 错误 404.3 - Not Found

    解决方法,以管理员身份进入命令行模式,运行: "%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ ...

  7. WCF服务上应用protobuf

    WCF服务上应用protobuf Web api  主要功能: 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, updat ...

  8. WCF服务寄宿到IIS

    一.WCF简介: Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台.整合了原有的wind ...

  9. 如何建立一个WCF服务并将其发布到IIS上

    在我们的软件开发中,经常会连接到数据库中,如果是常规的操作,我们经常会将连接数据库的字符串写在配置文件中,然后去读取数据库的连接字符串,其实这种方式是非常不科学的,这会直接暴露我们的数据库,直接暴露我 ...

随机推荐

  1. SQL数据库之“TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2)”

    一.介绍 样本:TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2) 解析:TIMESTAMPDIFF(格式,开始时间,结束时间) 二.参数解析 格式: ...

  2. Netty之非阻塞处理

    Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络 IO 程序. 一.异步模型 同步I/O : 需要进程去真正的去操作I/O: 异步I/O:内核在I/O操作完成后 ...

  3. 如何正确的阅读Datasheet?

    不仅仅是芯片,包括工具.设备几乎任何电子产品,都需要去阅读它的datasheet,除了包括最低.最高要求,特点,建议和用途及其兼容的设备等等,更重要的是原厂商以一个成功者的身份去告诉你一些注意事项. ...

  4. html5中常被忘记的标签,属性

    placeholder placeholder是input中的属性,就是默认输入的text,当用户输入时,text会被清空. 用法 <input type ="text" p ...

  5. HTML5 & CSS3 内容收集(1)

    1. HTML发展历史介绍 2. 浏览器支持 2.1 新增标签支持 在html5 中新增了很多的标签,其中包括8个新增语义结构标签.header, section, footer, aside, na ...

  6. 移动端H5页面中1px边框的几种解决方法

    问题提出 这是一个比较老的问题了,我第一次注意到的时候,是UI设计师来找我麻烦,emmm那时候我才初入前端职场,啥也不懂啊啊啊啊啊,情形是这样的:设计师拿着手机过来:这些边框都粗了啊,我的设计稿上是1 ...

  7. Linux 0.11源码阅读笔记-内存管理

    内存管理 Linux内核使用段页式内存管理方式. 内存池 物理页:物理空闲内存被划分为固定大小(4k)的页 内存池:所有空闲物理页组成内存池,以页为单位进行分配回收.并通过位图记录了每个物理页是否空闲 ...

  8. SpringMVC获取请求参数-集合类型

    1.创建User实体类 ```java public class User { private String username; private int age; public String getU ...

  9. spring-基于xml的aop开发-快速入门

    1.导入aop的相关坐标 <dependency> <groupId>org.springframework</groupId> <artifactId> ...

  10. 百度离线人脸识别sdk的使用

    1.1下载sdk运行 百度离线人脸识别sdk的使用 1.2配置环境 添加至项目,可以拖动复制或者以类库形式添加face-resource此文件夹 放到根目录上一层 激活文件与所有dll引用放到根目录嫌 ...