在 .NET Core 和 .NET 5+ 中,不再支持 WCF 服务。如果仍需要使用 .NET 6 开发基于 SOAP 的 Web 服务,则有两个选项:
CoreWCF:CoreWCF 是 Windows Communication Foundation (WCF) 服务端到 .NET Core 的端口。 SoapCore:SoapCore 是一个用于使用 .NET Core/.NET 5+ 创建 SOAP 服务的库。 在本文中,我将向您展示如何使用 SoapCore 创建基于 SOAP 的 Web 服务,以及如何使用基于 SOAP 的 Web 服务。
解决方案结构

演示解决方案中有 3 个项目:
服务层:这是一个库项目。它包含服务功能的接口和实现。在此项目中,我们定义了一个 ProfileService。 SoapService:这是一个使用“ASP.NET Core Empty”项目模板创建的 Web 项目。在这个项目中,我们需要引用 “Service Layer” 项目和 SoapCore nuget 包:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="SoapCore" Version="1.1.0.37" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\ServiceLayer\ServiceLayer.csproj" />
  </ItemGroup>
</Project>
服务层项目 在本项目中,我们定义了 IProfileService 接口及其实现:
轮廓:
namespace ServiceLayer
{
    [DataContract]
    public class Profile
    {
        public Profile() { }
        [DataMember]
        public int ProfileID { get; set; }
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public string Email { get; set; }
        [DataMember]
        public string HomeAddress { get; set; }
    }
}
IProfileService 接口:
using System.ServiceModel;
namespace ServiceLayer
{
    [ServiceContract]
    public interface IProfileService
    {
        [OperationContract]
        [FaultContract(typeof(MissingProfileFault))]
        Profile GetProfile(int profileID);
    }
}
您需要使用 [ServiceContract] 属性修饰接口,并使用 [OperationContract] 属性修饰每个方法。需要添加以下 NuGet 包引用才能使用这些属性:
<ItemGroup>
  <PackageReference Include="System.ServiceModel.Primitives" Version="4.8.1" />
</ItemGroup>
IProfileService 接口的 GetProfile() 方法有一个 fault 属性,用于定义它可能引发的故障类型。
using System.Runtime.Serialization;
namespace ServiceLayer
{
    [DataContract]
    public class MissingProfileFault
    {
        public MissingProfileFault(string message)
        {
            this.Message = message;
        }
        [DataMember]
        public string Message { get; set; }
    }
}
IProfileService 实现:
namespace ServiceLayer
{
    public class ProfileService : IProfileService
    {
        private List<Profile> _profiles = new List<Profile>()
        {
            new Profile() { FirstName = "Foo", LastName = "Profile1", Email = "fooprofiile1@yahoo.com", HomeAddress = "1 King Street West, Toronto, ON", ProfileID = 1 },
            new Profile() { FirstName = "Bar", LastName = "Profile2", Email = "barprofiile2@gmail.com", HomeAddress = "1 Hollywood Avenue, Los Angeles, CA", ProfileID = 2 },
        };
        public Profile GetProfile(int profileID)
        {
            var profile = _profiles.FirstOrDefault(p => p.ProfileID == profileID);
            if (profile == null)
            {
                throw new FaultException<MissingProfileFault>(new MissingProfileFault($"A profile with ID {profileID} is missing."),
                    new FaultReason($"A profile with ID {profileID} is missing."),
                    new FaultCode("MissingProfile"), null);
            }
            return profile;
        }
    }
}
此项目使用 SoapCore 将 ProfileService 公开为基于 SOAP 的 Web 服务。程序 .cs 文件如下所示:
using Microsoft.Extensions.DependencyInjection.Extensions;
using ServiceLayer;
using SoapCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSoapCore();
builder.Services.TryAddSingleton<ProfileService>();
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints => { 
    endpoints.UseSoapEndpoint<ProfileService>(
        path: "/ProfileService.asmx", 
        encoder: new SoapEncoderOptions(), 
        serializer: SoapSerializer.DataContractSerializer, 
        caseInsensitivePath: true); });
app.Run();
在终结点中注意。UseSoapEndpoint
“path”参数指定服务的 URL 路径。您可以指定任何验证 URL 路径(例如 /profileservice.svc)。
默认情况下,路径区分大小写。如果将路径指定为“/ProfileService.asmx”,并在 URL 中键入“profileservice.asmx”,则会收到 404 错误,您可以通过将“caseInsensitivePath”参数指定为 true 来使其不区分大小写。
生成并运行此项目后,将打开浏览器并显示 404 错误。这是因为网站根目录下没有内容。

您可以在地址中附加我们在 program.cs 文件中指定的服务路径。您应该能够看到此服务的 WSDL 内容。

ProfileService 元数据 (WSDL) 将上述内容保存到本地驱动器的 profileservice.wsdl 文件中。
Soap 客户端 这是使用基于 SOAP 的 Web 服务的客户端应用。您可以通过两种不同的方式生成客户服务代码:
使用 Visual Studio
右键单击项目,然后选择“添加”->“服务参考”

在新的“添加新的 WCF Web 服务引用”对话框中,提供服务 URL 并指定要在其中生成的代码的命名空间。单击“下一步”->“下一步->完成”。

服务引用客户端代码将在“连接的服务”文件夹下生成。

使用 dotnet-svcutil 工具
使用 Visual Studio 生成代码需要你有权访问实时服务。在拥有多层基础架构的大公司中,情况可能并非如此。您可能只能从服务团队获取 WSDL 文件。在这种情况下,需要使用 dotnet CLI 工具手动生成代码。
可以使用名为 dotnet-svcutil 的工具。它类似于 .NET Framework 项目的服务模型元数据 — svcutil 工具。可以将 dotnet-svcutil NuGet 包安装为 CLI 工具:You can Install the dotnet-svcutil NuGet package as a CLI tool:
dotnet tool install --global dotnet-svcutil
成功安装此 CLI 工具后,您可以使用它来生成客户端服务参考代码。
dotnet-svcutil C:\Downloads\ProfileService.wsdl -n ",SoapServiceDemo.ProfileService" -n 参数指定生成的代码的命名空间。第一部分“”表示映射所有 targetNamespace,而无需显式映射到 CLR 命名空间(第二部分:“SoapServiceDemo.ProfileService”)。
代码在当前目录\ServiceReference\Reference.cs 中生成。
调用 SOAP Web 服务 将 reference.cs 文件添加到 SoapClient 项目后,可以将以下代码添加到 program.cs 文件中。
using (var client = new SoapServiceDemo.ProfileService.ProfileServiceClient())
{
    try
    {
        var profile = await client.GetProfileAsync(3);
        if (profile != null)
        {
            Console.WriteLine($"First Name: {profile.FirstName}");
            Console.WriteLine($"Last Name: {profile.LastName}");
            Console.WriteLine($"Email Address: {profile.Email}");
            Console.WriteLine($"Home Address: {profile.HomeAddress}");
        }
    }
    catch (FaultException<MissingProfileFault> ex)
    {
        Console.WriteLine($"Fault reason: {ex.Reason.ToString()}, fault code: {ex.Code.Name}, detail: {ex.Detail.Message}");
    }
    catch (FaultException ex)
    {
        Console.WriteLine(ex.ToString());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}
处理故障异常
GetProfile() 方法引发的错误是 MissingProfileFault(由 IProfileService 接口中的 FaultContract 属性定义)。在客户端中,捕获的异常将是 FaultException