WCF 客户端概述

本节描述客户端应用程序可以做什么,如何配置、创建和使用 Windows Communication Foundation (WCF) 客户端,以及如何保护客户端应用程序。

使用 WCF 客户端对象

客户端应用程序是使用 WCF 客户端与其他应用程序通信的托管应用程序。 为 WCF 服务创建一个客户端应用程序需要执行下列步骤:

  1. 获取服务终结点的服务协定、绑定以及地址信息。

  2. 使用该信息创建 WCF 客户端。

  3. 调用操作。

  4. 关闭 WCF 客户端对象。

以下部分将讨论上述这些步骤,并简单介绍以下问题:

  • 处理错误。

  • 配置和保护客户端。

  • 为双工服务创建回调对象。

  • 异步调用服务。

  • 使用客户端通道调用服务。

获取服务协定、绑定和地址

在 WCF 中,服务和客户端使用托管属性、接口和方法对协定进行建模。 若要连接客户端应用程序中的服务,则需要获取该服务协定的类型信息。 通常,使用 ServiceModel 元数据实用工具 (Svcutil.exe) 来获得服务协定的类型信息。 该实用工具会从服务中下载元数据,并使用你选择的语言将其转换到托管源代码文件中,同时还创建一个你可用于配置 WCF 客户端对象的客户端应用程序配置文件。 例如,如果你准备创建一个 WCF 客户端对象来调用 MyCalculatorService,并且你知道该服务的元数据已在 http://computerName/MyCalculatorService/Service.svc?wsdl 中发布,则下面的代码示例演示如何使用 Svcutil.exe 获取一个包含托管代码中的服务协定的 ClientCode.vb 文件。

svcutil /language:vb /out:ClientCode.vb /config:app.config http://computerName/MyCalculatorService/Service.svc?wsdl  

你可以将此协定代码编译为客户端应用程序或另一个程序集,然后,客户端应用程序可以使用该程序集创建一个 WCF 客户端对象。 可以使用配置文件配置客户端对象以与服务正确连接。

有关此过程的示例,请参阅如何:创建客户端。 有关协定的更完整信息,请参阅协定

创建一个 WCF 客户端对象

WCF 客户端是表示某个 WCF 服务的一个本地对象,客户端可以使用这种表示形式与远程服务进行通信。 WCF 客户端类型可实现目标服务协定,因此在你创建一个服务协定并配置它之后,就可以直接使用该客户端对象调用服务操作。 WCF 运行时将方法调用转换为消息,然后将这些消息发送到服务,侦听回复,并将这些值作为返回值或 out 参数(或 ref 参数)返回到 WCF 客户端对象中。

你还可以使用 WCF 客户端通道对象连接和使用服务。 有关详细信息,请参阅 WCF 客户端体系结构

创建一个新的 WCF 对象

为了演示如何使用 ClientBase<TChannel> 类,现假设已从服务应用程序生成了下面的简单服务协定。

注意

如果你是使用 Visual Studio 创建 WCF 客户端,则当你将一个服务引用添加到项目中时,对象将自动加载到对象浏览器中。

[System.ServiceModel.ServiceContractAttribute(
  Namespace = "http://microsoft.wcf.documentation"
)]
public interface ISampleService
{
    [System.ServiceModel.OperationContractAttribute(
      Action = "http://microsoft.wcf.documentation/ISampleService/SampleMethod",
      ReplyAction = "http://microsoft.wcf.documentation/ISampleService/SampleMethodResponse"
    )]
    [System.ServiceModel.FaultContractAttribute(
      typeof(microsoft.wcf.documentation.SampleFault),
      Action = "http://microsoft.wcf.documentation/ISampleService/SampleMethodSampleFaultFault"
    )]
    string SampleMethod(string msg);
}

如果你不是使用 Visual Studio,则请检查已生成的协定代码以查找扩展 ClientBase<TChannel> 的类型以及服务协定接口 ISampleService。 在这种情况下,该类型看上去类似下列代码:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class SampleServiceClient : System.ServiceModel.ClientBase<ISampleService>, ISampleService
{

    public SampleServiceClient()
    {
    }

    public SampleServiceClient(string endpointConfigurationName)
        :
            base(endpointConfigurationName)
    {
    }

    public SampleServiceClient(string endpointConfigurationName, string remoteAddress)
        :
            base(endpointConfigurationName, remoteAddress)
    {
    }

    public SampleServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress)
        :
            base(endpointConfigurationName, remoteAddress)
    {
    }

    public SampleServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
        :
            base(binding, remoteAddress)
    {
    }
    public string SampleMethod(string msg)
    {
        return base.Channel.SampleMethod(msg);
    }
}

可以通过使用其中一个构造函数将此类创建为一个本地对象,并对该本地对象进行配置,然后使用它连接到 ISampleService 类型的服务。

建议你首先创建 WCF 客户端对象,然后使用该对象并在一个单独的 try/catch 块内将它关闭。 请勿使用 using 语句(在 Visual Basic 中为 Using),因为该语句在某些故障模式下会屏蔽异常。 有关详细信息,请参阅以下部分以及使用“关闭”和“中止”发布 WCF 客户端资源

协定、绑定和地址

在可以创建 WCF 客户端对象之前,必须配置客户端对象。 特别是,客户端对象必须具有一个可以使用的服务终结点。 终结点由服务协定、绑定和地址组成。 (有关终结点的详细信息,请参阅终结点:地址、绑定和协定。)通常,这些信息位于客户端应用程序配置文件(例如 Svcutil.exe 工具生成的配置文件)的 <终结点> 元素中,当你创建客户端对象时将自动加载。 两种 WCF 客户端类型都具有使你能够以编程方式指定此信息的重载。

例如,上述示例中所使用的 ISampleService 的生成的配置文件包含以下终结点信息。

<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_ISampleService" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:01:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8080/SampleService" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_ISampleService" contract="ISampleService"
                name="WSHttpBinding_ISampleService">
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

此配置文件在 <client> 元素中指定目标终结点。 有关使用多个目标终结点的详细信息,请参阅 ClientBase<TChannel>ChannelFactory<TChannel> 构造函数。

调用操作

创建并配置了客户端对象后,请创建一个 try/catch 块,如果该对象是本地对象,则以相同的方式调用操作,然后关闭 WCF 客户端对象。 当客户端应用程序调用第一个操作时,WCF 将自动打开基础通道,并在回收对象时关闭基础通道。 (或者,还可以在调用其他操作之前或之后显式打开和关闭该通道。)

例如,如果您具有以下服务协定:

namespace Microsoft.ServiceModel.Samples  
{  
    using System;  
    using System.ServiceModel;  
  
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]  
    public interface ICalculator  
   {  
        [OperationContract]  
        double Add(double n1, double n2);  
        [OperationContract]  
        double Subtract(double n1, double n2);  
        [OperationContract]  
        double Multiply(double n1, double n2);  
        [OperationContract]  
        double Divide(double n1, double n2);  
    }  
}  
Namespace Microsoft.ServiceModel.Samples  
  
    Imports System  
    Imports System.ServiceModel  
  
    <ServiceContract(Namespace:= _  
    "http://Microsoft.ServiceModel.Samples")> _
   Public Interface ICalculator  
        <OperationContract> _
        Function Add(n1 As Double, n2 As Double) As Double  
        <OperationContract> _
        Function Subtract(n1 As Double, n2 As Double) As Double  
        <OperationContract> _
        Function Multiply(n1 As Double, n2 As Double) As Double  
        <OperationContract> _
     Function Divide(n1 As Double, n2 As Double) As Double  
End Interface  

你可以通过创建一个 WCF 客户端对象并调用其方法来调用操作,如以下代码示例所示。 打开、调用和关闭 WCF 客户端对象发生在一个 try/catch 块内。 有关详细信息,请参阅使用 WCF 客户端访问服务使用“关闭”和“中止”发布 WCF 客户端资源

CalculatorClient wcfClient = new CalculatorClient();
try
{
    Console.WriteLine(wcfClient.Add(4, 6));
    wcfClient.Close();
}
catch (TimeoutException timeout)
{
    // Handle the timeout exception.
    wcfClient.Abort();
}
catch (CommunicationException commException)
{
    // Handle the communication exception.
    wcfClient.Abort();
}

处理错误

当打开基础客户端通道(无论是通过显式打开还是通过调用操作自动打开)、使用客户端或通道对象调用操作,或关闭基础客户端通道时,都会在客户端应用程序中出现异常。 除了由操作返回的 SOAP 错误导致引发的任何 System.TimeoutException 对象外,建议您至少将应用程序设置为能够处理可能的 System.ServiceModel.CommunicationExceptionSystem.ServiceModel.FaultException 异常。 操作协定中指定的 SOAP 错误将作为 System.ServiceModel.FaultException<TDetail> 在客户端应用程序中引发,此异常中的类型参数为 SOAP 错误的详细信息类型。 有关在客户端应用程序中处理错误条件的详细信息,请参阅发送和接收错误。 有关演示如何在客户端处理错误的完整示例,请参阅预期异常

配置和保护客户端

若要配置客户端,请首先为客户端或通道对象加载目标终结点信息,通常是从配置文件中加载该信息,但是也可以使用客户端构造函数和属性以编程方式加载。 但是,若要启用特定的客户端行为或实施一些安全方案还需要执行其他配置步骤。

例如,服务协定的安全需求已在服务协定接口中声明,并且如果 Svcutil.exe 已创建了一个配置文件,则该文件通常会包含一个能够支持服务安全需求的绑定。 但是在某些情况中,可能需要更多的安全配置,例如配置客户端凭据。 有关 WCF 客户端安全配置的完整信息,请参阅保护客户端

此外,在客户端应用程序中还可以启用一些自定义修改,例如自定义运行时行为。 有关如何配置自定义客户端行为的详细信息,请参阅配置客户端行为

为双工服务创建回调对象

双工服务指定一个回调协定,客户端应用程序必须实现该协定以便提供一个该服务能够根据协定要求调用的回调对象。 虽然回调对象不是完整的服务(例如,您无法使用回调对象启动一个通道),但是为了实现和配置,这些回调对象可以被视为一种服务。

双工服务的客户端必须:

  • 实现一个回调协定类。

  • 创建回调协定实现类的一个实例,并使用该实例创建传递给 WCF 客户端构造函数的 System.ServiceModel.InstanceContext 对象。

  • 调用操作并处理操作回调。

双工 WCF 客户端对象除了会公开支持回调所必需的功能(包括回调服务的配置)以外,其他的功能和它们的非双工对应项相同。

例如,可以通过使用回调类的 System.ServiceModel.CallbackBehaviorAttribute 属性 (Attribute) 的属性 (Property),控制回调对象运行时行为的各个方面。 另一个示例是使用 System.ServiceModel.Description.CallbackDebugBehavior 类将异常信息返回给调用回调对象的服务。 有关详细信息,请参阅双工服务。 有关完整示例,请参阅双工

在运行 Internet 信息服务 (IIS) 5.1 的 Windows XP 计算机上,双工客户端必须使用 System.ServiceModel.WSDualHttpBinding 类指定一个客户端基址,否则会引发异常。 下面的代码示例演示如何在代码中执行此操作。

WSDualHttpBinding dualBinding = new WSDualHttpBinding();
EndpointAddress endptadr = new EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server");
dualBinding.ClientBaseAddress = new Uri("http://localhost:8000/DuplexTestUsingCode/Client/");

Dim dualBinding As New WSDualHttpBinding()
Dim endptadr As New EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server")
dualBinding.ClientBaseAddress = New Uri("http://localhost:8000/DuplexTestUsingCode/Client/")

下面的代码演示如何在配置文件中执行此操作。

<client>
  <endpoint
    name ="ServerEndpoint"
    address="http://localhost:12000/DuplexUsingConfig/Server"
    bindingConfiguration="WSDualHttpBinding_IDuplex"
    binding="wsDualHttpBinding"
    contract="IDuplex"
/>
</client>
<bindings>
  <wsDualHttpBinding>
    <binding
      name="WSDualHttpBinding_IDuplex"
      clientBaseAddress="http://localhost:8000/myClient/"
    />
  </wsDualHttpBinding>
</bindings>

异步调用服务

如何调用操作完全取决于客户端开发人员。 这是因为当在托管代码中表示组成操作的消息时,这些消息可以映射到同步或异步方法中。 因此,如果想要生成异步调用操作的客户端,则可以使用 Svcutil.exe 通过 /async 选项生成异步客户端代码。 有关详细信息,请参阅如何:以异步方式调用服务操作

使用 WCF 客户端通道调用服务

WCF 客户端类型扩展 ClientBase<TChannel>,而其自身派生自 System.ServiceModel.IClientChannel 接口,从而可以公开基础通道系统。 可以同时使用目标服务协定和 System.ServiceModel.ChannelFactory<TChannel> 类来调用服务。 有关详细信息,请参阅 WCF 客户端体系结构

另请参阅