Skip to content

C# 多线程 09-使用异步 I/O 04-异步调用 WCF 服务

🏷️ 《C# 多线程》

TIP

示例代码需要添加对 System.ServiceModel 的引用。

示例代码

csharp
/// <summary>
/// 异步调用 WCF 服务
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    ServiceHost host = null;

    try
    {
        // 创建一个的主机(服务类型为 HelloWorldService;承载服务的地址为 http://localhost:1234/HelloWorld)
        host = new ServiceHost(typeof(HelloWorldService), new Uri(SERVICE_URL));
        var metadata = host.Description.Behaviors.Find<ServiceMetadataBehavior>() ?? new ServiceMetadataBehavior();
        // 允许通过 HTTPGET 获取元数据
        // (即可以通过浏览器访问 http://localhost:1234/HelloWorld?wsdl 或 http://localhost:1234/HelloWorld?singleWsdl 获取 xml 格式的元数据)
        metadata.HttpGetEnabled = true;
        // 指定正在使用的 WS-Policy 规范的版本。
        metadata.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
        host.Description.Behaviors.Add(metadata);

        host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

        var endpoint = host.AddServiceEndpoint(typeof(IHelloWorldService), new BasicHttpBinding(), SERVICE_URL);

        host.Faulted += (sender, e) => Console.WriteLine("Error!");

        // 开启主机
        host.Open();

        Console.WriteLine($"Greeting service is running and listening on: {endpoint.Address} ({endpoint.Binding.Name})");

        var client = RunServiceClient();
        client.GetAwaiter().GetResult();
        
        Console.WriteLine("Press Enter to exit");
        Console.ReadLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error in catch block: {ex}");
    }
    finally
    {
        if (null != host)
        {
            if (host.State == CommunicationState.Faulted)
            {
                host.Abort();
            }
            else
            {
                host.Close();
            }
        }
    }
}

const string SERVICE_URL = "http://localhost:1234/HelloWorld";

/// <summary>
/// 模拟远程访问 WCF 服务
/// </summary>
/// <returns></returns>
static async Task RunServiceClient()
{
    var endpoint = new EndpointAddress(SERVICE_URL);
    // 创建服务通道
    var channel = ChannelFactory<IHelloWorldServiceClient>.CreateChannel(new BasicHttpBinding(), endpoint);

    // 通过通道异步访问 Greet 服务
    var greeting = await channel.GreetAsync("Eugene");
    Console.WriteLine(greeting);
}

/// <summary>
/// 服务端接口
/// </summary>
[ServiceContract(Namespace = "Packt", Name = "HelloWorldServiceContract")]
public interface IHelloWorldService
{
    [OperationContract]
    string Greet(string name);
}

/// <summary>
/// 客户端服务接口
/// </summary>
[ServiceContract(Namespace = "Packt", Name = "HelloWorldServiceContract")]
public interface IHelloWorldServiceClient
{
    [OperationContract]
    string Greet(string name);

    [OperationContract]
    Task<string> GreetAsync(string name);
}

/// <summary>
/// 服务端实现
/// </summary>
public class HelloWorldService : IHelloWorldService
{
    public string Greet(string name)
    {
        return $"Greetings, {name}";
    }
}

运行结果

注意

运行需要管理员权限(使用管理员权限打开 VS,或者使用管理员权限运行编译生成的 exe 文件)

txt
Greeting service is running and listening on: http://localhost:1234/HelloWorld (
BasicHttpBinding)
Greetings, Eugene
Press Enter to exit

HelloWorldService 服务页面

服务地址:http://localhost:1234/HelloWorld

已创建服务。

若要测试此服务,需要创建一个客户端,并将其用于调用该服务。可以使用下列语法,从命令行中使用 svcutil.exe 工具来进行此操作:

bash
svcutil.exe http://localhost:1234/HelloWorld?wsdl

您还可以访问作为单个文件的服务说明:

这将生成一个配置文件和一个包含客户端类的代码文件。请将这两个文件添加到客户端应用程序,并使用生成的客户端类来调用服务。例如:

C#

csharp
class Test
{
    static void Main()
    {
        HelloWorldServiceContractClient client = new HelloWorldServiceContractClient();

        // 使用 "client" 变量在服务上调用操作。

        // 始终关闭客户端。
        client.Close();
    }
}

Visual Basic

vb
Class Test
    Shared Sub Main()
        Dim client As HelloWorldServiceContractClient = New HelloWorldServiceContractClient()
        ' 使用 "client" 变量在服务上调用操作。

        ' 始终关闭客户端。
        client.Close()
    End Sub
End Class