Обработка исключений и сбоевHandling Exceptions and Faults

Исключения используются, чтобы передать сведения об ошибках локально в службе или реализации клиента.Exceptions are used to communicate errors locally within the service or the client implementation. С другой стороны, сбои используются, чтобы передать ошибки за пределы службы, например, от сервера клиенту или наоборот.Faults, on the other hand, are used to communicate errors across service boundaries, such as from the server to the client or vice versa. Помимо сбоев каналы транспорта часто используют механизмы, связанные с транспортом, чтобы сообщить об ошибках на транспортном уровне.In addition to faults, transport channels often use transport-specific mechanisms to communicate transport-level errors. Например, транспорт HTTP использует коды состояния, такие как «404», чтобы сообщить о несуществующем конечном URL-адресе (отсутствует конечная точка, чтобы вернуть ошибку).For example, HTTP transport uses status codes such as 404 to communicate a non-existing endpoint URL (there is no endpoint to send back a fault). Этот документ состоит из трех разделов, в которых содержится руководство для разработчиков пользовательских каналов.This document consists of three sections that provide guidance to custom channel authors. В первом разделе содержится руководство о том, когда и как определять и выдавать исключения.The first section provides guidance on when and how to define and throw exceptions. Во втором разделе содержится руководство по созданию и использованию ошибок.The second section provides guidance around generating and consuming faults. В третьем разделе разъясняется, как предоставить данные трассировки, которые помогут пользователю созданного канала устранить неполадки в выполняемых приложениях.The third section explains how to provide trace information to aid the user of your custom channel in troubleshooting running applications.

ИсключенияExceptions

Важно учитывать два момента при создании исключения: во-первых, его тип должен позволять пользователям написать правильный программный код, который будет соответствующим образом реагировать на исключение.There are two things to keep in mind when throwing an exception: First it has to be of a type that allows users to write correct code that can react appropriately to the exception. Во-вторых, исключение должно содержать достаточно сведений для пользователя, чтобы понять, какой произошел сбой, его последствия и возможные способы устранения.Second, it has to provide enough information for the user to understand what went wrong, the failure impact, and how to fix it. Следующие разделы содержат рекомендации по типам исключений и сообщениям для каналов Windows Communication Foundation (WCF).The following sections give guidance around exception types and messages for Windows Communication Foundation (WCF) channels. Общие инструкции об исключениях в .NET можно также найти в документе «Правила разработки исключений».There is also general guidance around exceptions in .NET in the Design Guidelines for Exceptions document.

Типы исключенияException Types

Все исключения, создаваемые каналами, должны быть типа System.TimeoutException, System.ServiceModel.CommunicationException или типа, унаследованного от CommunicationException.All exceptions thrown by channels must be either a System.TimeoutException, System.ServiceModel.CommunicationException, or a type derived from CommunicationException. (Могут также создаваться такие исключения, как ObjectDisposedException, но только для того, чтобы указать на неправильное использование канала вызывающим кодом.(Exceptions such as ObjectDisposedException may also be thrown, but only to indicate that the calling code has misused the channel. Если канал используется правильно, он должен вызвать только указанные исключения.) WCF предоставляет семь типов исключений, которые являются производными от CommunicationException и предназначены для использования каналами.If a channel is used correctly, it must only throw the given exceptions.) WCF provides seven exception types that derive from CommunicationException and are designed to be used by channels. Существуют другие исключения, унаследованные от CommunicationException, которые созданы для использования с другими частями системы.There are other CommunicationException-derived exceptions that are designed to be used by other parts of the system. Эти типы исключений приведены в следующей таблице.These exception types are:

Тип исключенияException Type ЗначениеMeaning Внутреннее содержимое исключенияInner Exception Content Стратегия восстановленияRecovery Strategy
AddressAlreadyInUseException Адрес конечной точки, указанный для прослушивания, уже используется.The endpoint address specified for listening is already in use. Если имеется, предоставляет дополнительные сведения об ошибке транспорта, вызвавшей это исключение.If present, provides more details about the transport error that caused this exception. Например, если выбран диапазон 10.0.0.0/20 для виртуальной сети, для пространства клиентских адресов можно выбрать 10.1.0.0/24.For example. PipeException, HttpListenerException или SocketException.PipeException, HttpListenerException, or SocketException. Повторите попытку с другим адресом.Try a different address.
AddressAccessDeniedException Доступ к адресу конечной точки, указанному для прослушивания, не разрешен процессом.The process is not allowed access to the endpoint address specified for listening. Если имеется, предоставляет дополнительные сведения об ошибке транспорта, вызвавшей это исключение.If present, provides more details about the transport error that caused this exception. Например, PipeException или HttpListenerException.For example, PipeException, or HttpListenerException. Повторите попытку с другими учетными данными.Try with different credentials.
CommunicationObjectFaultedException ICommunicationObjectИспользуемое состояние находится в состоянии Faulted (Дополнительные сведения см. в разделе сведения об изменениях состояния).The ICommunicationObject being used is in the Faulted state (for more information, see Understanding State Changes). Обратите внимание, когда объект с несколькими ожидающими вызовами переходит в состояние сбоя, только один вызов создает исключение, относящееся к сбою, а остальные вызовы создают исключение CommunicationObjectFaultedException.Note that when an object with multiple pending calls transitions to the Faulted state, only one call throws an exception that is related to the failure and the rest of the calls throw a CommunicationObjectFaultedException. Это исключение обычно создается потому, что приложение пропускает какое-либо исключение и пытается использовать объект с уже имеющимся сбоем, возможно находящимся не в том потоке, который перехватил исходное исключение.This exception is typically thrown because an application overlooks some exception and tries to use an already faulted object, possibly on a thread other than the one that caught the original exception. Если имеется, предоставляет сведения о внутреннем исключении.If present provides details about the inner exception. Создать новый объект.Create a new object. Обратите внимание, что в зависимости от причины сбоя ICommunicationObject, могут потребоваться другие действия для восстановления.Note that depending on what caused the ICommunicationObject to fault in the first place, there may be other work required to recover.
CommunicationObjectAbortedException ICommunicationObjectИспользованный объект был прерван (Дополнительные сведения см. в статье об изменениях состояния).The ICommunicationObject being used has been Aborted (for more information, see Understanding State Changes). Аналогично CommunicationObjectFaultedException , это исключение указывает, что приложение вызывается Abort для объекта, возможно, из другого потока, и объект больше не может использоваться по этой причине.Similar to CommunicationObjectFaultedException, this exception indicates the application has called Abort on the object, possibly from another thread, and the object is no longer usable for that reason. Если имеется, предоставляет сведения о внутреннем исключении.If present provides details about the inner exception. Создать новый объект.Create a new object. Обратите внимание, что в зависимости от причины прерывания ICommunicationObject, могут потребоваться другие действия для восстановления.Note that depending on what caused the ICommunicationObject to abort in the first place, there may be other work required to recover.
EndpointNotFoundException Удаленная целевая конечная точка не выполняет прослушивание.The target remote endpoint is not listening. Это может быть вызвано тем, что адреса конечной точки неверен или неразрешим, либо конечная точка недоступна.This can result from any part of the endpoint address being incorrect, irresolvable, or the endpoint being down. Примерами являются ошибка DNS, недоступность диспетчера очередей и незапущенная служба.Examples include DNS error, Queue Manager not available, and service not running. Внутреннее исключение предоставляет сведения, как правило, из используемого транспорта.The inner exception provides details, typically from the underlying transport. Повторите попытку с другим адресом.Try a different address. Пользователь также может повторить попытку после небольшой паузы, если служба была недоступна.Alternatively, the sender may wait a while and try again in case the service was down
ProtocolException Протоколы связи, описанные в политике конечной точки, не совпадают между конечными точками.The communication protocols, as described by the endpoint's policy, are mismatched between endpoints. Например, не совпадают типы содержимого кадрирования или превышен максимальный размер сообщения.For example, framing content type mismatch or max message size exceeded. Если имеется, предоставляет дополнительную информацию о конкретной ошибке протокола.If present provides more information about the specific protocol error. Например, QuotaExceededException - внутреннее исключение, создаваемое когда причина ошибки превышает MaxReceivedMessageSize.For example, QuotaExceededException is the inner exception when the error cause is exceeding MaxReceivedMessageSize. Восстановление: убедитесь в соответствии параметров протоколов отправителя и получателя.Recovery: Ensure sender and received protocol settings match. Одним из способов сделать это является повторный импорт метаданных конечной точки службы (политика) и использование созданной привязки для повторного создания канала.One way to do this is to re-import the service endpoint's metadata (policy) and use the generated binding to recreate the channel.
ServerTooBusyException Удаленная конечная точка выполняет прослушивание, но не готова к обработке сообщений.The remote endpoint is listening but is not prepared to process messages. Если имеется, внутренне исключение предоставляет ошибку протокола SOAP или сведения об ошибке на транспортном уровне.If present, the inner Exception provides the SOAP fault or transport-level error details. Восстановление: повторите операцию после небольшой паузы.Recovery: Wait and retry the operation later.
TimeoutException Не удалось завершить операцию в течение времени ожидания.The operation failed to complete within the timeout period. Может содержать сведение о времени ожидания.May provide details about the timeout. Повторите операцию после небольшой паузы.Wait and retry the operation later.

Определите новый тип исключения только в том случае, если данный тип соответствует конкретной стратегии восстановления, отличной от всех существующих типов исключений.Define a new exception type only if that type corresponds to a specific recovery strategy different from all of the existing exception types. В случае определения нового типа исключения, он должен быть унаследован от CommunicationException или одного из его производных классов.If you do define a new exception type, it must derive from CommunicationException or one of its derived classes.

Сообщения об исключенииException Messages

Сообщения об исключениях предназначены для пользователя, а не для программы, поэтому в них должно содержаться достаточно сведений, чтобы помочь пользователю понять и решить проблему.Exception messages are targeted at the user not the program so they should provide sufficient information to help the user understand and solve the problem. Ниже приведены три неотъемлемые части хорошего сообщения об исключении.The three essential parts of a good exception message are:

Что произошло.What happened. Предоставьте четкое описание проблемы с помощью терминов, относящихся к интерфейсу пользователя.Provide a clear description of the problem using terms that relate to the user's experience. Например, "Недопустимый раздел конфигурации" является примером неправильного сообщения об исключении.For example, a bad exception message would be "Invalid configuration section". В этом случае у пользователя нет сведений о том, какой раздел и почему является неправильным.This leaves the user wondering which configuration section is incorrect and why it is incorrect. Улучшенное сообщение будет иметь вид "Недопустимый раздел конфигурации <customBinding> ".An improved message would be "Invalid configuration section <customBinding>". Примером еще более точного сообщения может быть "Не удается добавить транспорт с именем myTransport в привязку myBinding, так как в привязке уже есть транспорт с именем myTransport".An even better message would be "Cannot add the transport named myTransport to the binding named myBinding because the binding already has a transport named myTransport". Это очень конкретное сообщение с использованием терминов и имен, которые пользователь может легко найти в файле конфигурации приложения.This is a very specific message using terms and names that the user can easily identify in the application's configuration file. Однако в сообщении по-прежнему не хватает нескольких ключевых компонентов.However, there are still a few key components missing.

Значимость ошибки.The significance of the error. Если в сообщении четко не указано значение ошибки, у пользователя может возникнуть вопрос, является ли ошибка неустранимой, либо ее можно игнорировать.Unless the message states clearly what the error means, the user is likely to wonder whether it is a fatal error or if it can be ignored. Как правило, в сообщениях должно выводиться значение и значимость ошибки.In general, messages should lead with the meaning or significance of the error. Предыдущий пример может быть улучшен следующим образом: «Не удалось открыть ServiceHost из-за ошибки конфигурации. Не удается добавить транспорт с имением myTransport в привязку myBinding, так как в привязке уже есть транспорт с именем myTransport».To improve the previous example, the message could be "ServiceHost failed to Open due to a configuration error: Cannot add the transport named myTransport to the binding named myBinding because the binding already has a transport named myTransport".

Способы устранения проблемы пользователем.How the user should correct the problem. Самая важная часть сообщения - помочь пользователю устранить проблему.The most important part of the message is helping the user fix the problem. В сообщении должны содержаться некоторые рекомендации или указания о том, что необходимо проверить или исправить, чтобы устранить проблему.The message should include some guidance or hints about what to check or fix to remedy the problem. Например, «Не удалось открыть ServiceHost из-за ошибки конфигурации. Не удается добавить транспорт с имением myTransport в привязку myBinding, так как в привязке уже есть транспорт с именем myTransport.For example, "ServiceHost failed to Open due to a configuration error: Cannot add the transport named myTransport to the binding named myBinding because the binding already has a transport named myTransport. Убедитесь в том, что в привязке есть только один транспорт».Please ensure there is only one transport in the binding".

Информирование об ошибкахCommunicating Faults

Протоколы SOAP 1.1 и SOAP 1.2 определяют конкретную структуру для ошибок.SOAP 1.1 and SOAP 1.2 both define a specific structure for faults. Между двумя спецификациями существуют определенные отличия, но для создания и использования ошибок обычно применяются типы Message и MessageFault.There are some differences between the two specifications but in general, the Message and MessageFault types are used to create and consume faults.

Обработка исключений и ошибокHandling exceptions and faults
Ошибка SOAP 1.2 (слева) и ошибка SOAP 1.1 (справа).SOAP 1.2 Fault (left) and SOAP 1.1 Fault (right). Обратите внимание, что только в SOAP 1.1 элемент с ошибкой перечислен в пространстве имен.Note that in SOAP 1.1 only the Fault element is namespace qualified.

Протокол SOAP определяет сообщение об ошибке, как сообщение, содержащее элемент с ошибкой (элемент с именем <env:Fault>), в качестве дочернего элемента <env:Body>.SOAP defines a fault message as a message that contains only a fault element (an element whose name is <env:Fault>) as a child of <env:Body>. Содержимое элемента с ошибкой немного отличается в протоколах SOAP 1.1 и SOAP 1.2, как показано на рисунке 1.The contents of the fault element differ slightly between SOAP 1.1 and SOAP 1.2 as shown in figure 1. Однако класс System.ServiceModel.Channels.MessageFault нормализует эти различия в одной модели объекта:However, the System.ServiceModel.Channels.MessageFault class normalizes these differences into one object model:

public abstract class MessageFault  
{  
    protected MessageFault();  
  
    public virtual string Actor { get; }  
    public virtual string Node { get; }  
    public static string DefaultAction { get; }  
    public abstract FaultCode Code { get; }  
    public abstract bool HasDetail { get; }  
    public abstract FaultReason Reason { get; }  
  
    public T GetDetail<T>();  
    public T GetDetail<T>( XmlObjectSerializer serializer);  
    public System.Xml.XmlDictionaryReader GetReaderAtDetailContents();  
  
    // other methods omitted  
}  

Свойство Code соответствует свойству env:Code (или faultCode в SOAP 1.1) и определяет тип ошибки.The Code property corresponds to the env:Code (or faultCode in SOAP 1.1) and identifies the type of the fault. Протокол SOAP 1.2 определяет пять допустимых значений для faultCode (например, отправитель и получатель), а также определяет элемент Subcode, который может содержать значение дополнительного кода.SOAP 1.2 defines five allowable values for faultCode (for example, Sender and Receiver) and defines a Subcode element which can contain any subcode value. (Список допустимых кодов ошибок и их значения см. в спецификации SOAP 1,2 .) Протокол SOAP 1,1 имеет немного другой механизм: он определяет четыре faultCode значения (например, клиент и сервер), которые можно расширить либо путем определения совершенно новых, либо с помощью точечной нотации для создания более конкретных faultCodes , например Client. Authentication.(See the SOAP 1.2 specification for the list of allowable fault codes and their meaning.) SOAP 1.1 has a slightly different mechanism: It defines four faultCode values (for example, Client and Server) that can be extended either by defining entirely new ones or by using the dot notation to create more specific faultCodes, for example, Client.Authentication.

При использовании типа MessageFault для программирования ошибок, значение FaultCode.Name и FaultCode.Namespace сопоставляется с именем и пространством имен env:Code в SOAP 1.2 или faultCode в SOAP 1.1.When you use MessageFault to program faults, the FaultCode.Name and FaultCode.Namespace maps to the name and namespace of the SOAP 1.2 env:Code or the SOAP 1.1 faultCode. Значение FaultCode.SubCode сопоставляется со значением env:Subcode для протокола SOAP 1.2, а для SOAP 1.1 оно равно NULL.The FaultCode.SubCode maps to env:Subcode for SOAP 1.2 and is null for SOAP 1.1.

Если требуется различать ошибку программными средствами, необходимо создать новые дополнительные коды ошибок (или новые коды ошибок при использовании протокола SOAP 1.1).You should create new fault subcodes (or new fault codes if using SOAP 1.1) if it is interesting to programmatically distinguish a fault. Это аналогично созданию нового типа исключения.This is analogous to creating a new exception type. Не следует использовать запись через точку с кодами ошибок SOAP 1.1.You should avoid using the dot notation with SOAP 1.1 fault codes. ( Базовый профиль WS-I также не рекомендует использовать нотацию с точкой кода ошибки.)(The WS-I Basic Profile also discourages the use of the fault code dot notation.)

public class FaultCode  
{  
    public FaultCode(string name);  
    public FaultCode(string name, FaultCode subCode);  
    public FaultCode(string name, string ns);  
    public FaultCode(string name, string ns, FaultCode subCode);  
  
    public bool IsPredefinedFault { get; }  
    public bool IsReceiverFault { get; }  
    public bool IsSenderFault { get; }  
    public string Name { get; }  
    public string Namespace { get; }  
    public FaultCode SubCode { get; }  
  
//  methods omitted  
  
}  

ReasonСвойство соответствует env:Reason (или faultString в SOAP 1,1) понятному описанию условия ошибки, аналогичного сообщению исключения.The Reason property corresponds to the env:Reason (or faultString in SOAP 1.1) a human-readable description of the error condition analogous to an exception's message. Класс FaultReasonenv:Reason/faultStringв протоколе SOAP) имеет встроенную поддержку нескольких переводов для обеспечения глобализации.The FaultReason class (and SOAP env:Reason/faultString) has built-in support for having multiple translations in the interest of globalization.

public class FaultReason  
{  
    public FaultReason(FaultReasonText translation);  
    public FaultReason(IEnumerable<FaultReasonText> translations);  
    public FaultReason(string text);  
  
    public SynchronizedReadOnlyCollection<FaultReasonText> Translations
    {
       get;
    }  
  
 }  

Содержимое сведений об ошибке предоставляется в MessageFault с помощью различных методов, включая GetDetail <T> и GetReaderAtDetailContents ().The fault detail contents are exposed on MessageFault using various methods including the GetDetail<T> and GetReaderAtDetailContents(). Сведения об ошибке — это непрозрачный элемент для передачи дополнительных данных об ошибке.The fault detail is an opaque element for carrying additional detail about the fault. Это полезно, если существуют произвольные структурированные сведения, которые необходимо передать с ошибкой.This is useful if there is some arbitrary structured detail that you want to carry with the fault.

Создание ошибокGenerating Faults

В данном разделе объясняется процесс создания ошибки в ответ на условие ошибки, обнаруженное в канале или свойстве сообщения, созданном каналом.This section explains the process of generating a fault in response to an error condition detected in a channel or in a message property created by the channel. Типичным примером является отправка обратно ошибки в ответ на сообщение запроса, содержащее недопустимые данные.A typical example is sending back a fault in response to a request message that contains invalid data.

При создании ошибки пользовательский канал должен не отправлять ее напрямую, а вызвать исключение и позволить вышестоящему уровню решить, необходимо ли преобразовывать это исключение в ошибку и как ее отправлять.When generating a fault, the custom channel should not send the fault directly, rather, it should throw an exception and let the layer above decide whether to convert that exception to a fault and how to send it. Для облегчения данного преобразования канал должен предоставить реализацию FaultConverter, которая может преобразовать исключение, вызываемое пользовательским каналом, в соответствующую ошибку.To aid in this conversion, the channel should provide a FaultConverter implementation that can convert the exception thrown by the custom channel to the appropriate fault. FaultConverter определяется следующим образом.FaultConverter is defined as:

public class FaultConverter  
{  
    public static FaultConverter GetDefaultFaultConverter(  
                                   MessageVersion version);  
    protected abstract bool OnTryCreateFaultMessage(  
                                   Exception exception,
                                   out Message message);  
    public bool TryCreateFaultMessage(  
                                   Exception exception,
                                   out Message message);  
}  

Каждый канал, создающий пользовательские ошибки, должен реализовать преобразователь FaultConverter и вернуть его из вызова в GetProperty<FaultConverter>.Each channel that generates custom faults must implement FaultConverter and return it from a call to GetProperty<FaultConverter>. Пользовательская OnTryCreateFaultMessage реализация должна либо преобразовать исключение в ошибку, либо передать его в объект с внутренним каналом FaultConverter .The custom OnTryCreateFaultMessage implementation must either convert the exception to a fault or delegate to the inner channel's FaultConverter. Если канал является транспортным, он должен либо преобразовать исключение или делегировать в кодировщик, FaultConverter либо значение по умолчанию, FaultConverter предоставленное в WCF.If the channel is a transport it must either convert the exception or delegate to the encoder's FaultConverter or the default FaultConverter provided in WCF . Преобразователь по умолчанию FaultConverter преобразует ошибки, соответствующие сообщениям об ошибках, которые указаны в WS-Addressing и SOAP.The default FaultConverter converts errors corresponding to fault messages specified by WS-Addressing and SOAP. Ниже приведен пример реализации OnTryCreateFaultMessage.Here is an example OnTryCreateFaultMessage implementation.

public override bool OnTryCreateFaultMessage(Exception exception,
                                             out Message message)  
{  
    if (exception is ...)  
    {  
        message = ...;  
        return true;  
    }  
  
#if IMPLEMENTING_TRANSPORT_CHANNEL  
    FaultConverter encoderConverter =
                    this.encoder.GetProperty<FaultConverter>();  
    if ((encoderConverter != null) &&
        (encoderConverter.TryCreateFaultMessage(  
         exception, out message)))  
    {  
        return true;  
    }  
  
    FaultConverter defaultConverter =
                   FaultConverter.GetDefaultFaultConverter(  
                   this.channel.messageVersion);  
    return defaultConverter.TryCreateFaultMessage(  
                   exception,
                   out message);  
#else  
    FaultConverter inner =
                   this.innerChannel.GetProperty<FaultConverter>();  
    if (inner != null)  
    {  
        return inner.TryCreateFaultMessage(exception, out message);  
    }  
    else  
    {  
        message = null;  
        return false;  
    }  
#endif  
}  

В этом шаблоне показано, что исключения, вызываемые между уровнями для требующих ошибок условий, должны содержать достаточно сведений для соответствующего генератора ошибок, чтобы создать правильную ошибку.An implication of this pattern is that exceptions thrown between layers for error conditions that require faults must contain enough information for the corresponding fault generator to create the correct fault. Разработчики пользовательских каналов могут определять типы исключений, соответствующие условиям ошибок, если такие исключения не существуют.As a custom channel author, you may define exception types that correspond to different fault conditions if such exceptions do not already exist. Обратите внимание, что исключения, затрагивающий разные уровни канала, должны передавать условие ошибки, а не непрозрачные данные ошибки.Note that exceptions traversing channel layers should communicate the error condition rather than opaque fault data.

Категории ошибокFault Categories

Обычно существует три категории ошибок.There are generally three categories of faults:

  1. Ошибки, распространяющиеся на весь стек.Faults that are pervasive throughout the entire stack. Эти ошибки встречаются на любом уровне в стеке каналов, например, InvalidCardinalityAddressingException.These faults could be encountered at any layer in the channel stack, for example InvalidCardinalityAddressingException.

  2. Ошибки, встречающиеся выше определенного уровня в стеке. Например, некоторые ошибки, относящиеся к поточной транзакции или ролям безопасности.Faults that can be encountered anywhere above a certain layer in the stack for example some errors that pertain to a flowed transaction or to security roles.

  3. Ошибки, направленные на один уровень в стеке, например, ошибки номера последовательности WS-RM.Faults that are directed at a single layer in the stack, for example errors like WS-RM sequence number faults.

Категория 1.Category 1. Как правило, ошибки WS-Addressing и SOAP.Faults are generally WS-Addressing and SOAP faults. Базовый FaultConverter класс, предоставляемый WCF, преобразует ошибки, соответствующие сообщениям об ошибках, заданным в WS-Addressing и SOAP, поэтому нет необходимости самостоятельно выполнять преобразование этих исключений.The base FaultConverter class provided by WCF converts errors corresponding to fault messages specified by WS-Addressing and SOAP so you do not have to handle conversion of these exceptions yourself.

Категория 2.Category 2. Ошибки происходят, когда уровень добавляет в сообщение свойство, которое не полностью использует сведения о сообщении, относящиеся к данному уровню.Faults occur when a layer adds a property to the message that does not completely consume message information that pertains to that layer. Ошибки могут быть обнаружены позже, когда расположенный выше уровень запрашивает свойство сообщения для дальнейшей обработки сведений о сообщении.Errors may be detected later when a higher layer asks the message property to process message information further. Такие каналы должны реализовать свойство GetProperty, заданное ранее, чтобы разрешить расположенному выше уровню отправлять обратно правильную ошибку.Such channels should implement the GetProperty specified previously to enable the higher layer to send back the correct fault. Примером этого может служить свойство TransactionMessageProperty.An example of this is the TransactionMessageProperty. Это свойство добавляется в сообщение без полной проверки всех данных в заголовке (для проверки может понадобиться обратиться к координатору распределенных транзакций (DTC).This property is added to the message without fully validating all the data in the header (doing so may involve contacting the distributed transaction coordinator (DTC).

Категория 3.Category 3. Ошибки создаются и отправляются только одним уровнем в процессоре.Faults are only generated and sent by a single layer in the processor. Таким образом, все исключения содержатся на одном уровне.Therefore all the exceptions are contained within the layer. Чтобы улучшить согласованность между каналами и упростить обслуживание, в пользовательском канале следует использовать приведенный ранее шаблон для создания сообщений об ошибках даже для внутренних ошибок.To improve consistency among channels and ease maintenance, your custom channel should use the pattern specified previously to generate fault messages even for internal faults.

Интерпретация полученных ошибокInterpreting Received Faults

В данном разделе содержится руководство по созданию соответствующего исключения при получении сообщения об ошибке.This section provides guidance for generating the appropriate exception when receiving a fault message. Ниже приведено дерево принятия решений для обработки сообщения на каждом из уровней стека.The decision tree for processing a message at every layer in the stack is as follows:

  1. Если слой считает, что сообщение является недопустимым, слой должен выполнить обработку "Недопустимое сообщение".If the layer considers the message to be invalid, the layer should do its 'invalid message' processing. Данная обработка зависит от уровня, но она может включать удаление сообщения, трассировку или вызов исключения, преобразуемого в ошибку.Such processing is specific to the layer but could include dropping the message, tracing, or throwing an exception that gets converted to a fault. Примерами являются случаи, когда безопасность получает сообщение с ненадлежащей защитой, или диспетчер ресурсов получает сообщение с неправильным порядковым номером.Examples include security receiving a message that is not secured properly, or RM receiving a message with a bad sequence number.

  2. В противном случае, если сообщение является сообщением об ошибке, которое относится к слою, а сообщение не имеет смысла за пределами взаимодействия слоя, этот слой должен обработать условие ошибки.Otherwise, if the message is a fault message that applies specifically to the layer, and the message is not meaningful outside the layer's interaction, the layer should handle the error condition. Примером этого является ошибка «отказ последовательности диспетчера ресурсов», являющаяся бессмысленной для уровней, расположенных выше канала диспетчера ресурсов. Это подразумевает сбой канала диспетчера ресурсов и вызов из незавершенных операций.An example of this is an RM Sequence Refused fault that is meaningless to layers above the RM channel and that implies faulting the RM channel and throwing from pending operations.

  3. В противном случае сообщение должно быть возвращено из запроса() или получения().Otherwise, the message should be returned from Request() or Receive(). Сюда также входят случаи, когда уровень распознает ошибку, но ошибка указывает на сбой запроса и не подразумевает сбой канала и вызов из незавершенных операций.This includes cases where the layer recognizes the fault, but the fault just indicates that a request failed and does not imply faulting the channel and throwing from pending operations. Чтобы упростить использование, уровень должен реализовать GetProperty<FaultConverter> и возвратить производный класс FaultConverter, который может преобразовать исключение, переопределив OnTryCreateException.To improve usability in such a case, the layer should implement GetProperty<FaultConverter> and return a FaultConverter derived class that can convert the fault to an exception by overriding OnTryCreateException.

Приведенные ниже объектные модели поддерживают преобразование сообщений в исключения.The following object model supports converting messages to exceptions:

public class FaultConverter  
{  
    public static FaultConverter GetDefaultFaultConverter(  
                                  MessageVersion version);  
    protected abstract bool OnTryCreateException(  
                                 Message message,
                                 MessageFault fault,
                                 out Exception exception);  
    public bool TryCreateException(  
                                 Message message,
                                 MessageFault fault,
                                 out Exception exception);  
}  

Уровень канала может реализовать GetProperty<FaultConverter>, чтобы поддержать преобразование сообщений об ошибках в исключения.A channel layer can implement GetProperty<FaultConverter> to support converting fault messages to exceptions. Для этого переопределите OnTryCreateException и проверьте сообщение об ошибке.To do so, override OnTryCreateException and inspect the fault message. При подтверждении выполните преобразование, в противном случае запросите преобразование у внутреннего канала.If recognized, do the conversion, otherwise ask the inner channel to convert it. Каналы транспорта должны делегировать FaultConverter.GetDefaultFaultConverter получение FaultConverter для SOAP и WS-Addressing.Transport channels should delegate to FaultConverter.GetDefaultFaultConverter to get the default SOAP/WS-Addressing FaultConverter.

Типичная реализация выглядит следующим образом.A typical implementation looks like this:

public override bool OnTryCreateException(  
                            Message message,
                            MessageFault fault,
                            out Exception exception)  
{  
    if (message.Action == "...")  
    {  
        exception = ...;  
        return true;  
    }  
    // OR  
    if ((fault.Code.Name == "...") && (fault.Code.Namespace == "..."))  
    {  
        exception = ...;  
        return true;  
    }  
  
    if (fault.IsMustUnderstand)  
    {  
        if (fault.WasHeaderNotUnderstood(  
                   message.Headers, "...", "..."))  
        {  
            exception = new ProtocolException(...);  
            return true;  
        }  
    }  
  
#if IMPLEMENTING_TRANSPORT_CHANNEL  
    FaultConverter encoderConverter =
              this.encoder.GetProperty<FaultConverter>();  
    if ((encoderConverter != null) &&
        (encoderConverter.TryCreateException(  
                              message, fault, out exception)))  
    {  
        return true;  
    }  
  
    FaultConverter defaultConverter =  
             FaultConverter.GetDefaultFaultConverter(  
                             this.channel.messageVersion);  
    return defaultConverter.TryCreateException(  
                             message, fault, out exception);  
#else  
    FaultConverter inner =
                    this.innerChannel.GetProperty<FaultConverter>();  
    if (inner != null)  
    {  
        return inner.TryCreateException(message, fault, out exception);  
    }  
    else  
    {  
        exception = null;  
        return false;  
    }  
#endif  
}  

Для конкретных условий ошибки, имеющих определенные сценарии восстановления, можно задать производный класс ProtocolException.For specific fault conditions that have distinct recovery scenarios, consider defining a derived class of ProtocolException.

Обработка MustUnderstandMustUnderstand Processing

Протокол SOAP определяет общую ошибку для оповещения о том, что необходимый заголовок не был понят получателем.SOAP defines a general fault for signaling that a required header was not understood by the receiver. Эта ошибка называется ошибкой mustUnderstand.This fault is known as the mustUnderstand fault. В WCF пользовательские каналы никогда не создают mustUnderstand ошибок.In WCF, custom channels never generate mustUnderstand faults. Вместо этого диспетчер WCF, расположенный в верхней части стека связи WCF, проверяет, что в базовом стеке были распознаны все заголовки, помеченные как MustUnderstand = true.Instead, the WCF Dispatcher, which is located at the top of the WCF communication stack, checks to see that all headers that were marked as MustUnderstand=true were understood by the underlying stack. Если какой-то из заголовков не был понят, в этой точке создается ошибка mustUnderstand.If any were not understood, a mustUnderstand fault is generated at that point. (Пользователь может отключить обработку mustUnderstand, и приложение будет принимать все заголовки сообщений.(The user can choose to turn off this mustUnderstand processing and have the application receive all message headers. В этом случае приложение отвечает за выполнение mustUnderstand обработки.) Сформированная ошибка включает заголовок Нотундерстуд, содержащий имена всех заголовков с MustUnderstand = true, которые не были распознаны.In that case the application is responsible for performing mustUnderstand processing.) The generated fault includes a NotUnderstood header that contains the names of all headers with MustUnderstand=true that were not understood.

Если канал протокола отправляет пользовательский заголовок со значением MustUnderstand=true и получает ошибку mustUnderstand, он должен определить, вызвана ли эта ошибка отправленным заголовком.If your protocol channel sends a custom header with MustUnderstand=true and receives a mustUnderstand fault, it must figure out whether that fault is due to the header it sent. Для этого можно использовать два члена в классе MessageFault:There are two members on the MessageFault class that are useful for this:

public class MessageFault  
{  
    ...  
    public bool IsMustUnderstandFault { get; }  
    public static bool WasHeaderNotUnderstood(MessageHeaders headers,
        string name, string ns) { }  
    ...  
  
}  

IsMustUnderstandFault возвращает значение true, если это ошибка mustUnderstand.IsMustUnderstandFault returns true if the fault is a mustUnderstand fault. WasHeaderNotUnderstood возвращает значение true, если заголовок с указанным именем и пространством имен включен в ошибку в качестве заголовка NotUnderstood.WasHeaderNotUnderstood returns true if the header with the specified name and namespace is included in the fault as a NotUnderstood header. В противном случае возвращается значение false.Otherwise, it returns false.

Если канал выдает заголовок со значением MustUnderstand = true, уровень также должен реализовать шаблон API для создания исключения и преобразовать ошибки mustUnderstand, вызванные этим заголовком, в более полезное исключение, описанное ранее.If a channel emits a header that is marked MustUnderstand = true, then that layer should also implement the Exception Generation API pattern and should convert mustUnderstand faults caused by that header to a more useful exception as described previously.

ТрассировкаTracing

Платформа .NET Framework обеспечивает механизм для трассировки выполнения программы, который полезен для диагностики приложений в производственной среде или периодических проблем, когда нет возможности использовать отладчик для пошаговой проверки кода.The .NET Framework provides a mechanism to trace program execution as a way to aid diagnosing production applications or intermittent problems where it is not possible to just attach a debugger and step through the code. Основные компоненты этого механизма расположены в пространстве имен System.Diagnostics и состоят из следующих элементов.The core components of this mechanism are in the System.Diagnostics namespace and consist of:

  • System.Diagnostics.TraceSource, являющийся источником записываемых данных трассировки, System.Diagnostics.TraceListener, являющийся абстрактным базовым классом для конкретных прослушивателей, которые получают подлежащие трассировке сведения из TraceSource и выводят их в назначение, зависящее от прослушивателя.System.Diagnostics.TraceSource, which is the source of trace information to be written, System.Diagnostics.TraceListener, which is an abstract base class for concrete listeners that receive the information to be traced from the TraceSource and output it to a listener-specific destination. Например, XmlWriterTraceListener выводит данные трассировки в XML-файл.For example, XmlWriterTraceListener outputs trace information to an XML file. Наконец, класс System.Diagnostics.TraceSwitch позволяет пользователю управлять детализацией трассировке и обычно задается в конфигурации.Finally, System.Diagnostics.TraceSwitch, which lets the application user control the tracing verbosity and is typically specified in configuration.

  • Помимо основных компонентов можно использовать средство Service Trace Viewer (SvcTraceViewer.exe) для просмотра трассировок WCF и поиска по ним.In addition to the core components, you can use the Service Trace Viewer Tool (SvcTraceViewer.exe) to view and search WCF traces. Это средство предназначено специально для файлов трассировки, создаваемых WCF, и записывается с помощью XmlWriterTraceListener .The tool is designed specifically for trace files generated by WCF and written out using XmlWriterTraceListener. На следующем рисунке показаны различные компоненты, задействованные с трассировке.The following figure shows the various components involved in tracing.

Обработка исключений и ошибокHandling exceptions and faults

Трассировка из пользовательского каналаTracing from a Custom Channel

Пользовательские каналы должны записывать сообщения трассировки, чтобы помочь с диагностикой проблем, когда невозможно применить отладчик на запущенном приложении.Custom channels should write out trace messages to assist in diagnosing problems when it is not possible to attach a debugger to the running application. Это предполагает две высокоуровневые задачи: создание экземпляра класса TraceSource и вызов его методов для записи трассировок.This involves two high level tasks: Instantiating a TraceSource and calling its methods to write traces.

При создании экземпляра класса TraceSource указываемая строка становится именем источника.When instantiating a TraceSource, the string you specify becomes the name of that source. Данное имя используется для настройки (включения/отключения/установки уровня трассировки) источника трассировки.This name is used to configure (enable/disable/set tracing level) the trace source. Оно также отображается непосредственно в выводе трассировки.It also appears in the trace output itself. Пользовательские каналы должны использовать уникальное имя источника, чтобы при чтении вывода трассировки можно быть понять, откуда поступают данные трассировки.Custom channels should use a unique source name to help readers of the trace output understand where the trace information comes from. Для имени источника трассировки принято использовать имя сборки, записывающей информацию.Using the name of the assembly that is writing the information as the name of the trace source is the common practice. Например, WCF использует System. ServiceModel в качестве источника трассировки для информации, записываемой из сборки System. ServiceModel.For example, WCF uses System.ServiceModel as the trace source for information written from the System.ServiceModel assembly.

После создания источника трассировки вызываются его методы TraceData, TraceEvent или TraceInformation, чтобы внести записи трассировки в прослушиватели трассировки.Once you have a trace source, you call its TraceData, TraceEvent, or TraceInformation methods to write trace entries to the trace listeners. Для каждой внесенной записи трассировки необходимо классифицировать тип события как один из типов, определенный в TraceEventType.For each trace entry you write, you need to classify the type of event as one of the event types defined in TraceEventType. Данная классификация и установка уровня трассировки определяют, выводится ли запись трассировки прослушивателю.This classification and the trace level setting in configuration determine whether the trace entry is output to the listener. Например, установка в конфигурации уровней трассировки Warning позволяет записывать трассировки Warning, Error и Critical, но блокировать записи «Данные» и «Подробно».For example, setting the trace level in configuration to Warning allows Warning, Error and Critical trace entries to be written but blocks Information and Verbose entries. Ниже приведен пример создания источника трассировки и внесения записи на уровне «Данные».Here is an example of instantiating a trace source and writing out an entry at Information level:

using System.Diagnostics;  
//...  
TraceSource udpSource = new TraceSource("Microsoft.Samples.Udp");  
//...  
udpsource.TraceInformation("UdpInputChannel received a message");  

Важно!

Настоятельно рекомендуется указать имя источника трассировки, являющееся уникальным для пользовательского канала, чтобы при считывании выходных данных трассировки было понятно, откуда они поступили.It is highly recommended that you specify a trace source name that is unique to your custom channel to help trace output readers understand where the output came from.

Интеграция со средством просмотра трассировкиIntegrating with the Trace Viewer

Трассировки, формируемые каналом, можно выводить в формате, доступном для чтения средством Service Trace Viewer (SvcTraceViewer.exe) , с помощью в System.Diagnostics.XmlWriterTraceListener качестве прослушивателя трассировки.Traces generated by your channel can be output in a format readable by the Service Trace Viewer Tool (SvcTraceViewer.exe) by using System.Diagnostics.XmlWriterTraceListener as the trace listener. Этим обычно не занимают разработчики каналов.This is not something you, as the channel developer, need to do. Вместо этого пользователь приложения (или пользователь, выполняющий устранение неполадок в приложении) должен настроить этот прослушиватель трассировки в файле конфигурации приложения.Rather, it is the application user (or the person troubleshooting the application) that needs to configure this trace listener in the application's configuration file. Например, следующая конфигурация выполняет вывод данных трассировки из пространства имен System.ServiceModel и Microsoft.Samples.Udp в файл с именем TraceEventsFile.e2e:For example, the following configuration outputs trace information from both System.ServiceModel and Microsoft.Samples.Udp to the file named TraceEventsFile.e2e:

<configuration>  
  <system.diagnostics>  
    <sources>  
      <!-- configure System.ServiceModel trace source -->  
      <source name="System.ServiceModel" switchValue="Verbose"
              propagateActivity="true">  
        <listeners>  
          <add name="e2e" />  
        </listeners>  
      </source>  
      <!-- configure Microsoft.Samples.Udp trace source -->  
      <source name="Microsoft.Samples.Udp" switchValue="Verbose" >  
        <listeners>  
          <add name="e2e" />  
        </listeners>  
      </source>  
    </sources>  
    <!--   
    Define a shared trace listener that outputs to TraceFile.e2e  
    The listener name is e2e   
    -->  
    <sharedListeners>  
      <add name="e2e" type="System.Diagnostics.XmlWriterTraceListener"  
        initializeData=".\TraceFile.e2e"/>  
    </sharedListeners>  
    <trace autoflush="true" />  
  </system.diagnostics>  
</configuration>  

Трассировка структурированных данныхTracing Structured Data

В классе System.Diagnostics.TraceSource имеется метод TraceData, который принимает один или несколько объектов для включения в запись трассировки.System.Diagnostics.TraceSource has a TraceData method that takes one or more objects that are to be included in the trace entry. Как правило, метод Object.ToString вызывается на каждом объекте, и результирующая строка записывается как часть записи.In general, the Object.ToString method is called on each object and the resulting string is written as part of the trace entry. При использовании System.Diagnostics.XmlWriterTraceListener для вывода данных трассировки можно передавать класс System.Xml.XPath.IXPathNavigable в виде объекта данных в TraceData.When using System.Diagnostics.XmlWriterTraceListener to output traces, you can pass an System.Xml.XPath.IXPathNavigable as the data object to TraceData. Результирующая запись трассировки содержит данные XML, предоставленные System.Xml.XPath.XPathNavigator.The resulting trace entry includes the XML provided by the System.Xml.XPath.XPathNavigator. Ниже приведен пример записи с данными приложения XML.Here is an example entry with XML application data:

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">  
  <System xmlns="...">  
    <EventID>12</EventID>  
    <Type>3</Type>  
    <SubType Name="Information">0</SubType>  
    <Level>8</Level>  
    <TimeCreated SystemTime="2006-01-13T22:58:03.0654832Z" />  
    <Source Name="Microsoft.ServiceModel.Samples.Udp" />  
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />  
    <Execution  ProcessName="UdpTestConsole"
                ProcessID="3348" ThreadID="4" />  
    <Channel />  
    <Computer>COMPUTER-LT01</Computer>  
  </System>  
<!-- XML application data -->  
  <ApplicationData>  
  <TraceData>  
   <DataItem>  
   <TraceRecord
     Severity="Information"  
     xmlns="…">  
        <TraceIdentifier>some trace id</TraceIdentifier>  
        <Description>EndReceive called</Description>  
        <AppDomain>UdpTestConsole.exe</AppDomain>  
        <Source>UdpInputChannel</Source>  
      </TraceRecord>  
    </DataItem>  
  </TraceData>  
  </ApplicationData>  
</E2ETraceEvent>  

Средство просмотра трассировки WCF понимает схему TraceRecord элемента, показанного ранее, и извлекает данные из своих дочерних элементов и отображает их в табличном формате.The WCF trace viewer understands the schema of the TraceRecord element shown previously and extracts the data from its child elements and displays it in a tabular format. Канал должен использовать эту схему при трассировке структурированных данных приложения, чтобы пользователи средства Svctraceviewer.exe могли считывать данные.Your channel should use this schema when tracing structured application data to help Svctraceviewer.exe users read the data.