WCF 疑难解答快速入门

本主题以问答形式描述出现的一些最常见问题、它们的解决方法以及可以从何处找到关于这些问题的更多信息。

问题

问:如果我的客户端在第一次请求后暂时处于空闲,有时我会在第二次请求时收到 MessageSecurityException。发生了什么情况?

第二次请求失败主要有两个原因:(1) 会话已超时或 (2) 承载服务的 Web 服务器被回收。在第一种情况下,会话在服务超时前有效。当服务在其绑定指定的时间段 (ReceiveTimeout) 内未接收到来自客户端的请求时,服务将终止安全会话。后续客户端消息会导致 MessageSecurityException。客户端必须重新建立与服务的安全会话,才能发送以后的消息或使用有状态安全上下文令牌。使用有状态安全上下文令牌,安全会话还可以使 Web 服务器免于回收。有关 在安全会话中使用有状态安全上下文令牌的更多信息,请参见如何:为安全会话创建有状态安全上下文令牌。或者,也可禁用安全会话。当使用 WsHttpBinding 绑定时,可以将 establishSecurityContext 属性设置为 false,以禁用安全会话。若要为其他绑定禁用安全会话,必须创建自定义绑定。有关创建自定义绑定的详细信息,请参见如何:使用 SecurityBindingElement 创建自定义绑定。在应用任何这些选择前,您必须先了解应用程序的安全要求。

问:大约有 10 个客户端与我的服务交互后,我的服务开始拒绝新的客户端。发生了什么情况?

默认情况下,服务最多只能有 10 个并发会话。因此,如果服务绑定使用会话,则服务将接受新的客户端连接,直到到达该数目;之后,它将拒绝新的客户端连接,直到当前会话之一结束。可以通过多种方式支持更多的客户端。如果您的服务不要求会话,则不要使用会话绑定。(有关更多信息,请参见 使用会话.) 另一种选择是增大会话限制,方法是将 MaxConcurrentSessions 属性的值更改为适合您环境的数字。

问:我是否可以从 WCF 应用程序的配置文件以外的某处加载我的服务配置?

是。不过,您必须创建自定义 ServiceHost 类,并重写 ApplyConfiguration 方法。在此方法内,您可以调用 base 先加载配置(如果您还要加载标准配置信息),但是也可以整个替换配置加载系统。请注意,如果要从一个不同于应用程序配置文件的配置文件中加载配置,则必须自行分析配置文件,然后加载配置。

下面的代码示例演示如何重写 ApplyConfiguration 方法以及直接配置终结点。

public class MyServiceHost : ServiceHost
{
  public MyServiceHost(Type serviceType, params Uri[] baseAddresses)  
    : base(serviceType, baseAddresses)
  { Console.WriteLine("MyServiceHost Constructor"); }

  protected override void ApplyConfiguration()
  {
    string straddress = GetAddress();
    Uri address = new Uri(straddress);
    Binding binding = GetBinding();
    base.AddServiceEndpoint(typeof(IData), binding, address);
  }

  string GetAddress()
  { return "http://MyMachine:7777/MyEndpointAddress/"; }

  Binding GetBinding()
  {
    WSHttpBinding binding = new WSHttpBinding();
    binding.Security.Mode = SecurityMode.None;
    return binding;
  }
}

问:我的服务和客户端工作得很好,但是当客户端位于另一台计算机上时,为何就无法正常工作?发生了什么情况?

根据异常的不同,可能会有多个问题:

  • 可能需要将客户端终结点的地址更改为主机名,而不是“localhost”。
  • 可能需要打开应用程序的端口。有关详细信息,请参见 SDK 示例中的Firewall Instructions
  • 有关其他可能的问题,请参见示例主题Running the Samples in a Workgroup and Across Machines
  • 如果客户端使用的是 Windows 凭据并且异常为 SecurityNegotiationException,则按如下方式配置 Kerberos。
    1. 将标识凭据添加到客户端的 App.config 文件中的终结点元素:

      <endpoint 
        address="http://MyServer:8000/MyService/" 
        binding="wsHttpBinding" 
        bindingConfiguration="WSHttpBinding_IServiceExample" 
        contract="IServiceExample" 
        behaviorConfiguration="ClientCredBehavior" 
        name="WSHttpBinding_IServiceExample">
        <identity>
          <userPrincipalName value="name@corp.contoso.com"/>
        </identity>
      </endpoint>
      
    2. 在 System 或 NetworkService 帐户下运行自承载服务。可以运行此命令在 System 帐户下创建命令窗口:

      at 12:36  /interactive "cmd.exe"
      
    3. 在 Internet 信息服务 (IIS) 下承载服务,默认情况下,IIS 使用服务主体名称 (SPN) 帐户。

    4. 使用 SetSPN 在域中注册一个新的 SPN。请注意,您需要具有域管理员的身份才能执行此操作。

有关 Kerberos 协议的更多信息,请参见 WCF 中使用的安全概念和:

问:当我引发一个类型为异常的 FaultException<异常> 时,我总是在客户端上接收到一个常规 FaultException 类型,而不是泛型类型。发生了什么情况?

强烈建议您创建自己的自定义错误数据类型,并在您的错误协定中将其声明为详细信息类型。原因是使用系统提供的异常类型:

  • 创建一个类型依赖,它将移除面向服务的应用程序中功能最强大的应用程序之一。
  • 不能依赖以标准方式进行序列化的异常。某些异常(如 SecurityException)可能根本无法序列化。
  • 向客户端公开内部实现的详细信息。有关更多信息,请参见 在协定和服务中指定和处理错误.

但是,如果您正在调试应用程序,则可以序列化异常信息,并使用 ServiceDebugBehavior 类将其返回到客户端。

问:当答复未包含任何数据时,单向操作与请求-答复操作的返回速度似乎基本相同。发生了什么情况?

指定一个操作为单向只表示操作协定接受输入消息,而不返回输出消息。在 WCF 中,当出站数据已写入网络或引发异常时,所有客户端调用都将返回。单向操作以相同方式工作,如果找不到服务则它们可以引发;或者如果服务没有准备好从网络接受数据则它们可以阻止。通常在 WCF 中,这会致使单向调用返回到客户端的速度比请求-答复快;但是减慢出站数据在网络上的发送速度的任何情况都会降低单向操作以及请求-答复操作的速度。有关更多信息,请参见 单向服务使用客户端访问服务

问:我对我的服务使用的是 X.509 证书,并且获得一个 System.Security.Cryptography.CryptographicException。发生了什么情况?

这通常发生在更改了 IIS 辅助进程运行时所使用的用户帐户之后。例如,在 Windows XP 中,如果将 Aspnet_wp.exe 运行时所使用的默认用户帐户 ASPNET 更改为自定义用户帐户,则您可能会看到此错误。如果使用的是私钥,则使用该私钥的进程需要具有访问存储该私钥的文件的权限。

如果的确如此,则必须向该进程的帐户授予对包含该私钥的文件的读访问特权。例如,如果 IIS 辅助进程运行在 Bob 帐户下,则您需要向 Bob 授予对包含该私钥的文件的读访问权。

有关 如何向正确的用户帐户授予对包含特定 X.509 证书的私钥的文件的访问权的更多信息,请参见如何:使 X.509 证书可由 WCF 访问

问:我将操作的第一个参数从大写更改为了小写,现在我的客户端引发一个异常。发生了什么情况?

操作签名中的参数名称值是协定的一部分且区分大小写。当需要区分本地参数名称与描述客户端应用程序操作的元数据时,请使用 System.ServiceModel.MessageParameterAttribute 属性。

问:我正在使用我的跟踪工具之一,并且获得一个 EndpointNotFoundException。发生了什么情况?

如果使用的跟踪工具不符合系统提供的 WCF 跟踪机制,并且收到一个指示存在地址筛选器不匹配的 EndpointNotFoundException,则需要使用 ClientViaBehavior 类将消息定向到跟踪实用工具,并让该实用工具将这些消息重定向到服务地址。ClientViaBehavior 类更改 Via 寻址标头,以独立于最终接收方(由 To 寻址标头指示)指定下一个网络地址。但是,执行此操作时请不要更改终结点地址,它用于设立 To 值。

下面的代码示例演示一个示例客户端配置文件。

<endpoint 
  address=https://localhost:8000/MyServer/
  binding="wsHttpBinding"
  bindingConfiguration="WSHttpBinding_IMyContract"
  behaviorConfiguration="MyClient" 
  contract="IMyContract" 
  name="WSHttpBinding_IMyContract">
</endpoint>
<behaviors>
  <endpointBehaviors>
    <behavior name="MyClient">
      <clientVia viaUri="https://localhost:8001/MyServer/"/>
    </behavior>
  </endpointBehaviors>
</behaviors>

问:何为基址?它如何关联到一个终结点地址?

基址是 ServiceHost 类的根地址。默认情况下,如果将一个 ServiceMetadataBehavior 类添加到您的服务配置中,主机发布的所有终结点的 Web 服务描述语言 (WSDL) 都将从 HTTP 基址、提供给元数据行为的任何相对地址以及“?wsdl”中检索。如果熟悉 ASP.NET 和 IIS,您将会看到基址等效于虚拟目录。

问:如何为一个服务公开多个终结点?

为此您可以:将 <endpoint> 元素添加到应用程序配置文件中的 <service> 元素;或以编程方式执行等效步骤。有关更多信息,请参见 指定终结点地址如何:在配置中创建服务终结点如何:在代码中创建服务终结点

问:如何知道哪些绑定支持不同的行为?

有关 系统提供的绑定和它们支持的功能的更多信息,请参见系统提供的绑定

问:如何使用不同的值为任何属性配置标准绑定?

有关更多信息,请参见为 Windows Communication Foundation 服务配置绑定

问:如果需要将 HTTP 用作传输,我是否需要将我的服务部署到 HTTP 服务器上?

否。系统提供的绑定包括在从任何主机应用程序类型使用时支持 HTTP 传输的多个绑定。有关 系统提供的绑定和它们支持的功能的更多信息,请参见系统提供的绑定

问:使用标准绑定的服务的默认行为是什么?

这取决于所选的标准绑定。通常,使用会话的绑定的默认行为是为每个新客户端创建一个新的服务实例,来自特殊客户端的后续调用将路由到关联的服务实例。有关 系统提供的绑定和它们支持的功能的更多信息,请参见系统提供的绑定

问:有没有简单方法来查看功能到绑定的映射?例如,我如何才能确定哪些绑定支持事务、安全性等?

有。请参见系统提供的绑定

问:我如何将自定义数据类型从服务传递到客户端?

在两个终结点之间传递的数据类型必须是可序列化的,针对服务的最简单、可互操作性最强的序列化机制是使用 DataContractAttributeDataMemberAttribute 类。有关 此序列化机制以及其他支持的序列化机制的更多信息,请参见在服务协定中指定数据传输

问:我何时该使用配置文件进行配置,何时该通过代码进行配置?

因为使用应用程序配置文件,开发人员可以将有关运行时配置的决策交由部署者掌管,所以可将那些决不应由部署者做出的决策用于产品代码中的配置。部署者可以是自己计算机上的单个安装程序,也可以是使用企业组策略修改计算机配置文件并针对本地修改锁定这些文件的企业管理员。有关后者的示例,请参见如何:在企业中锁定终结点

问:在设计性能良好的服务方面我应了解什么?

请参见设计和实现服务

问:生成的客户端代码中都有哪些资料?

若要了解生成的客户端代码,请参见了解生成的客户端代码。有关 客户端体系结构的更多信息,请参见 客户端体系结构

问:为什么应在 WCF 客户端对象上调用 Close 方法?

通过在 WCF 客户端对象调用 Close,客户端和服务可以妥善地结束对话并回收与其关联的资源。此外,如果正在使用会话,则调用 Close 可能是确定自上次调用以来会话是否出过错(这种方案对于客户端应用程序很有意义)的最快捷的方式。有关更多信息,请参见 使用 WCF 客户端访问服务和示例Expected Exceptions

问:为什么我的服务没有如我所期望的那样运行,即使代码看起来很正常时亦如此?

如果您的服务应用程序是使用应用程序配置文件配置的,则应检查该文件以确定某些配置元素或属性是否正以意外方式改变执行行为。特别是,运行时行为非常依赖实现终结点中的协定的绑定。在配置文件中确认该绑定可以按您期望的方式支持所需的功能。

如果配置文件看起来完好,则可以使用诊断功能(如日志记录和跟踪)继续检查应用程序的运行时行为。有关更多信息,请参见 管理和诊断.

问:通知客户端服务出现问题的最佳方式是什么?

执行此操作的最佳方式是将具有自定义可序列化错误数据类型的 FaultContractAttribute 类添加到操作中。之后,当操作遇到该类可检测到的错误条件时,该类将引发一个新的 FaultException,其中类型参数是可序列化错误类型。有关更多信息,请参见 在协定和服务中指定和处理错误.

问:应将哪些信息传回客户端?

仅返回服务的客户端需要了解的信息。作为服务的设计者,您应该只提供这部分信息,而不应提供更多的信息,以尽量减少向未经授权的客户端公开内部实现的详细信息。这就是强烈建议您不要在 SOAP 错误中返回 Exception 对象的原因。有关更多信息,请参见 在协定和服务中指定和处理错误.

问:我的客户端应用程序如何检测到与服务的连接已关闭?

可以处理客户端通道的 CommunicationObject 状态更改事件;但是,何时通知您通道关闭或出错取决于通道的实现情况。例如,NetTcpBinding 类可以迅速通知您通道已关闭或出错,因为其会话的生存期与基础套接字的生存期相关联。

但是,旨在保护应用程序免受轻微网络干扰的会话绑定,如 ReliableSessionBindingElement 类提供的会话,在尝试重新建立会话时可能有时不会通知您。由于这个原因,建议您不要尝试直接检测断开的连接。

而应将会话视为对话。如果打开通道,进行几次操作调用,然后成功关闭通道,则可以假定通道未以意外方式关闭。有关 客户端和会话的更多信息,请参见使用会话。有关 绑定客户端上与通道相关的异常的更多信息,请参见使用客户端访问服务。有关以常规方处理异常的信息,请参见在协定和服务中指定和处理错误

问:如何配置用于 WCF 服务安全的安全套接字层证书?

请参见如何:使用 SSL 证书配置端口

问:如何使用消息检查器来记录或修改消息?

请参见下面的主题:

问:如何添加有额外信息的终结点地址?

当您需要以编程方式附加复杂终结点地址(例如,如果您需要以编程方式指定一个包含特定标头或标识的 EndpointAddress 类)时,您必须执行以下操作,其中 relativeOrAbsoluteAddress 是相对或绝对统一资源标识符 (URI):

// Do this:
ServiceEndpoint e = myServiceHost.AddServiceEndpoint(c,b,"relativeOrAbsoluteAddress");
e.Address = new EndpointAddress(e.Address.Uri, /*other info like headers */);

问:我尝试生成 Web 承载的示例时,生成由于生成目录、复制命令或删除命令失败而失败。为什么会出现这种情况?

在生成过程中,某些 Web 承载的示例会尝试将已编译的 WCF 服务二进制文件复制到 %SystemDrive%\inetpub\wwwroot\ServiceModelSamples 文件夹。这实际上就是在 IIS 中部署该服务。如果运行 SDK 命令提示或 Visual Studio 的帐户无权修改该文件夹,则生成将中断。若要更正此错误,可以执行下列操作之一:

  • 向生成示例所使用的帐户授予对 %SystemDrive%\inetpub\wwwroot 的修改权限
  • 以管理员身份运行 SDK 命令提示或 Visual Studio。

另请参见

概念

调试 Windows 身份验证错误