Отправка и получение неисправностей

Ошибки SOAP передают сведения об условии ошибки из службы клиенту и в дуплексном случае от клиента к службе в режиме взаимодействия. Как правило, служба определяет настраиваемое содержимое ошибки и указывает, какие операции могут возвращать их. (Дополнительные сведения см. в разделе "Определение и указание ошибок".) В этом разделе описывается, как служба или дуплексный клиент может отправлять эти ошибки, когда произошло соответствующее условие ошибки, и как клиент или приложение службы обрабатывает эти ошибки. Общие сведения об обработке ошибок в приложениях Windows Communication Foundation (WCF) см. в разделе "Указание и обработка ошибок в контрактах и службах".

Отправка ошибок SOAP

Объявленные ошибки SOAP — это те, в которых операция содержит System.ServiceModel.FaultContractAttribute, указывающий на пользовательский тип сбоя SOAP. Необъявленные ошибки SOAP — это те, которые не указаны в контракте для операции.

Отправка сообщенных сбоев

Чтобы отправить объявленную ошибку SOAP, определите условие ошибки, для которой подходит ошибка SOAP, и сгенерируйте новый System.ServiceModel.FaultException<TDetail>, где параметр типа является новым объектом типа, указанного в FaultContractAttribute для этой операции. Следующий пример кода демонстрирует использование FaultContractAttribute для указания, что операция SampleMethod может возвращать SOAP-ошибку с типом детализации GreetingFault.

[OperationContract]
[FaultContractAttribute(
  typeof(GreetingFault),
  Action="http://www.contoso.com/GreetingFault",
  ProtectionLevel=ProtectionLevel.EncryptAndSign
  )]
string SampleMethod(string msg);
<OperationContract, FaultContractAttribute(GetType(GreetingFault), Action:="http://www.contoso.com/GreetingFault", ProtectionLevel:=ProtectionLevel.EncryptAndSign)> _
Function SampleMethod(ByVal msg As String) As String

Чтобы передать GreetingFault сведения об ошибке клиенту, перехватите соответствующее условие ошибки и создайте новый тип System.ServiceModel.FaultException<TDetail> с новым GreetingFaultGreetingFault объектом в качестве аргумента, как показано в следующем примере кода. Если клиент является клиентским приложением WCF, он сталкивается с этим как с управляемым исключением, тип которого System.ServiceModel.FaultException<TDetail> является GreetingFault.

throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred. You said: " + msg));
    Throw New FaultException(Of GreetingFault)(New GreetingFault("A Greeting error occurred. You said: " & msg))
End If

Отправка необъявленных ошибок

Отправка необъявленных ошибок может быть очень полезной для быстрой диагностики и отладки проблем в приложениях WCF, но его полезность в качестве средства отладки ограничена. Как правило, при отладке рекомендуется использовать свойство ServiceDebugBehavior.IncludeExceptionDetailInFaults. Если задать для этого параметра значение true, клиенты сталкиваются с такими ошибками, как FaultException<TDetail> исключения типа ExceptionDetail.

Это важно

Поскольку управляемые исключения могут раскрывать внутреннюю информацию приложения, установка ServiceBehaviorAttribute.IncludeExceptionDetailInFaults или ServiceDebugBehavior.IncludeExceptionDetailInFaults в значение true может позволить клиентам WCF получать сведения об исключениях внутренних операций службы, включая личную или другую конфиденциальную информацию.

Поэтому назначение ServiceBehaviorAttribute.IncludeExceptionDetailInFaults или ServiceDebugBehavior.IncludeExceptionDetailInFaults значению true рекомендуется только в качестве временного средства отладки приложения службы. Кроме того, WSDL для метода, возвращающего необработанные управляемые исключения таким образом, не содержит контракт для FaultException<TDetail> типа ExceptionDetail. Клиенты должны ожидать возможность неизвестного сбоя SOAP (возвращенного клиентам WCF в качестве System.ServiceModel.FaultException объектов) для правильного получения сведений об отладке.

Чтобы отправить необъявленную ошибку SOAP, создайте System.ServiceModel.FaultException объект (а не универсальный тип FaultException<TDetail>) и передайте строку конструктору. Это становится доступно клиентским приложениям WCF как выброшенное исключение System.ServiceModel.FaultException, где строка доступна посредством вызова метода FaultException<TDetail>.ToString.

Замечание

Если вы объявляете ошибку SOAP типа string, а затем выбрасываете ее в службе как FaultException<TDetail> типа System.String, строковое значение назначается свойству FaultException<TDetail>.Detail, и оно недоступно из FaultException<TDetail>.ToString.

Обработка ошибок

В клиентах WCF ошибки SOAP, возникающие во время обмена данными, интересующие клиентские приложения, возникают в виде управляемых исключений. Хотя во время выполнения любой программы может возникнуть множество исключений, приложения, использующие клиентская модель программирования WCF, могут ожидать обработки исключений из следующих двух типов в результате взаимодействия.

TimeoutException объекты выбрасываются, когда операция превышает указанный период времени ожидания.

CommunicationException объекты выбрасываются, когда в службе или на стороне клиента возникают условия ошибки связи, поддающиеся восстановлению.

Класс CommunicationException имеет два важных производных типа: FaultException, а также универсальный тип FaultException<TDetail>.

FaultException исключения возникают, когда обработчик получает ошибку, которая не ожидается и не указана в контракте операции; обычно это происходит при отладке приложения, когда у службы свойство ServiceDebugBehavior.IncludeExceptionDetailInFaults установлено в true.

FaultException<TDetail> исключения возникают на клиенте, когда ошибка, указанная в контракте операции, получается в ответ на двустороннюю операцию (то есть метод с атрибутом OperationContractAttribute, где IsOneWay установлено в false).

Замечание

Если в службе WCF ServiceBehaviorAttribute.IncludeExceptionDetailInFaults или свойство ServiceDebugBehavior.IncludeExceptionDetailInFaults установлено на значение true, клиент воспринимает это как необъявленное FaultException<TDetail> типа ExceptionDetail. Клиенты могут перехватывать эту конкретную ошибку или обрабатывать ошибку в блоке catch для FaultException.

Как правило, только исключения FaultException<TDetail>, TimeoutException и CommunicationException представляют интерес для клиентов и служб.

Замечание

Другие исключения, конечно, происходят. Непредвиденные исключения включают катастрофические сбои, такие как System.OutOfMemoryException; как правило, приложения не должны перехватывать такие исключения.

Перехват исключений сбоя в правильном порядке

Так как FaultException<TDetail> происходит от FaultException, а FaultException происходит от CommunicationException, необходимо обрабатывать эти исключения в правильном порядке. Например, если у вас есть блок try/catch, в котором сначала перехватывается CommunicationException, все указанные и неуказанные ошибки SOAP обрабатываются там; любые последующие блоки перехвата для обработки пользовательского FaultException<TDetail> исключения никогда не вызываются.

Помните, что одна операция может возвращать любое число указанных ошибок. Каждая ошибка является уникальным типом и должна обрабатываться отдельно.

Обработка исключений при закрытии канала

Большая часть предыдущего обсуждения связана с ошибками, отправленными в ходе обработки сообщений приложения, то есть сообщения, явно отправленные клиентом при вызове операций клиентского приложения с объектом клиента WCF.

Даже при утилизации местных объектов, объект может вызывать или маскировать исключения, возникающие во время процесса переработки. При использовании клиентских объектов WCF может произойти что-то подобное. При вызове операций вы отправляете сообщения по установленному подключению. Закрытие канала может вызывать исключения, если подключение не может быть чисто закрыто или уже закрыто, даже если все операции возвращаются должным образом.

Как правило, каналы клиентских объектов закрываются одним из следующих способов:

  • При повторном использовании клиентского объекта WCF.

  • При вызове ClientBase<TChannel>.Closeклиентского приложения.

  • При вызове ICommunicationObject.Closeклиентского приложения.

  • Когда клиентское приложение вызывает операцию, которая является завершающей операцией для сеанса.

Во всех случаях закрытие канала указывает каналу начать закрывать все базовые каналы, которые могут отправлять сообщения для поддержки сложных функций на уровне приложения. Например, если контракту требуются сеансы, привязка пытается установить сеанс, обмениваясь сообщениями со служебным каналом до тех пор, пока сеанс не будет установлен. При закрытии канала базовый канал сеанса уведомляет службу о завершении сеанса. В этом случае, если канал уже прерван, закрыт или недоступен (например, если сетевой кабель отключен), клиентский канал не может сообщить каналу службы о завершении сеанса и может привести к исключению.

Прерывание канала при необходимости

Поскольку закрытие канала может также вызывать исключения, рекомендуется не только перехватывать исключения сбоев в правильном порядке, но и прерывать канал, который использовался для вызова, в блоке catch.

Если ошибка передает сведения об ошибке, относящиеся к операции, и другие пользователи могут использовать его, нет необходимости прерывать канал (хотя эти случаи редки). Во всех остальных случаях рекомендуется прервать канал. Пример, демонстрирующий все эти моменты, см. в разделе "Ожидаемые исключения".

В следующем примере кода показано, как обрабатывать исключения сбоя SOAP в базовом клиентском приложении, включая объявленную ошибку и необъявленную ошибку.

Замечание

Этот пример кода не использует конструкцию using . Так как закрывающие каналы могут вызывать исключения, рекомендуется сначала создавать клиент WCF, а затем открывать, использовать и закрывать клиент WCF в том же блоке проб. Для получения дополнительной информации см. 'Обзор клиента WCF' и 'Использование закрытия и прерывания для выпуска клиентских ресурсов WCF'.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
  public static void Main()
  {
    // Picks up configuration from the config file.
    SampleServiceClient wcfClient = new SampleServiceClient();
    try
    {
      // Making calls.
      Console.WriteLine("Enter the greeting to send: ");
      string greeting = Console.ReadLine();
      Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));

      Console.WriteLine("Press ENTER to exit:");
      Console.ReadLine();

      // Done with service.
      wcfClient.Close();
      Console.WriteLine("Done!");
    }
    catch (TimeoutException timeProblem)
    {
      Console.WriteLine("The service operation timed out. " + timeProblem.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException<GreetingFault> greetingFault)
    {
      Console.WriteLine(greetingFault.Detail.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException unknownFault)
    {
      Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (CommunicationException commProblem)
    {
      Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
      Console.ReadLine();
      wcfClient.Abort();
    }
  }
}

Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation

Public Class Client
    Public Shared Sub Main()
        ' Picks up configuration from the config file.
        Dim wcfClient As New SampleServiceClient()
        Try
            ' Making calls.
            Console.WriteLine("Enter the greeting to send: ")
            Dim greeting As String = Console.ReadLine()
            Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))

            Console.WriteLine("Press ENTER to exit:")
            Console.ReadLine()

            ' Done with service. 
            wcfClient.Close()
            Console.WriteLine("Done!")
        Catch timeProblem As TimeoutException
            Console.WriteLine("The service operation timed out. " & timeProblem.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch greetingFault As FaultException(Of GreetingFault)
            Console.WriteLine(greetingFault.Detail.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch unknownFault As FaultException
            Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch commProblem As CommunicationException
            Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
            Console.ReadLine()
            wcfClient.Abort()
        End Try
    End Sub
End Class

См. также