Выдача FaultException с помощью обработчика пользовательских исключений EL WCF - PullRequest
4 голосов
/ 06 января 2012

Поэтому я пытаюсь использовать Enterprise Library в своем сервисе WCF, чтобы выполнить для меня часть работы, связанной с исключениями.

Моя идея состоит в том, чтобы установить « Пользовательский обработчик исключений », скажем, «NullReferenceException», и в «Пользовательском обработчике исключений» создать исключение FaultException.

Насколько я понимаю, это "новое" исключение будет затем передаваться по проводам, и я поймаю его на клиенте.

Некоторый код для лучшего понимания:

Служба WCF:

[ServiceContract(Name="MyService", ConfigurationName="MyNamespace.MyService")]
    [ExceptionShielding("PolicyName")]
    public interface IMyService
    {
        [OperationContract]
        [FaultContract(typeof(MyFaultContract))]
        string Method(String Param);
}

public class MyService : IMyService
    {
        public string Method(String Param)
        {
          throw new  NullReferenceException("code-created null message here");
        }
}

Пользовательский обработчик исключений: (Корпоративная библиотека)

public class MyExceptionHandler : IExceptionHandler
    {
        public MyExceptionHandler(NameValueCollection collection) { }

        public MyExceptionHandler() { }

        public System.Exception HandleException(System.Exception exception, 
                              Guid handlingInstanceId)
        {
                MyFaultContract details = new MyFaultContract();
                if (exception is NullReferenceException)
                {
                    details.ErrorCode = MyFaultCode.NullReferenceException;
                    details.OriginalMessage = exception.Message;
                    details.MyMessage = "Null Reference exception here!";
                }
                return new FaultException<MyFaultContract>(details);
            }
}

Файл конфигурации приложения, который отображает исключение NullReferenceException на «Обработчик пользовательских исключений»:

<exceptionPolicies>
      <add name="PolicyName">
        <exceptionTypes>
          <add name="NullReferenceException" type="System.NullReferenceException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add type="MyExceptionHandler, MyApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="MyExceptionHandler" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>

И, наконец, код клиента, который ожидает перехват этого FaultException:

MyService.MyServiceClient client = new MyService.MyServiceClient();
            client.Open();
            try
            {
                string result = client.Method(string parameter);
            }

            catch (System.ServiceModel.FaultException<MyService.MyFaultContract> ex)
            {
                // where I think exception should end up
            }
            catch (System.ServiceModel.FaultException ex)
            {
                // general FaultException
            }

Но вместо этого я получаю ProtocolException, заявляя, что требуется ответное действие:

Получено ответное сообщение для операции «Метод» с действием ». Тем не менее, ваш клиентский код требует действий «http://tempuri.org/MyService/MethodResponse'.

Что я делаю не так? Можно ли вернуть (не явно выдать) исключение FaultException с настраиваемым FaultContract в «Обработчик настраиваемых исключений»?

Любой совет приветствуется.

Обновление: Как видите, действие постобработки в «Обработчике нестандартных исключений» установлено в «ThrowNewException». Если я изменяю его на «NotifyRethrow», я больше не получаю «ProtocolException»! Вместо этого клиент перехватывает обычное «FaultException» (не пользовательский тип).

Теперь вопрос в том, почему оригинальное FaultException с пользовательским типом не проходит через провод.

Обновление 2 Одна вещь, которую я забыл упомянуть, это то, что моя служба WCF работает в ServiceHost, а не в IIS. Поэтому в основном у меня есть служба Windows, которая создает ServiceHost и предоставляет интерфейс Service1 через этот ServiceHost.

Причина, по которой я думаю, что это может быть как-то связано с этой проблемой, заключается в том, что Enterprise Library утверждает, что имеет дело с исключениями во всем приложении, а не только в границах служб. Может быть, это приведет к тому, что исключение будет выдано слишком поздно? Или не на правильном уровне?

Обновление 3
Спасибо за пост! Вы правы, единственная причина, по которой я связываюсь с пользовательским обработчиком, заключается в том, что я хочу установить значение MyFaultCode. Я попробовал ваш совет - настроил 2 обработчика.

First - это пользовательский обработчик, который перехватывает исключение NullReference. Затем создается новое исключение - MyApplicationException с полем MyFaultContract.

Затем я настроил второй обработчик - встроенный «обработчик исключений Fault Contract», который перехватывает MyApplicationException, создает новое FaultException и автоматически сопоставляет MyFaultcontract из MyApplicationException с вновь созданным FaultException.

Клиент WCF по-прежнему перехватывает общее исключение FaultException, а не пользовательское соглашение.

Ответы [ 2 ]

3 голосов
/ 07 января 2012

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

По моему мнению, он в конечном итоге чище, чем при использовании Ent Lib, на 1 меньше зависимости, и вам не нужно такое отображение ошибок в вашей конфигурации, а значит меньше XML.

Пример:

public class MyServiceErrorHandler : IErrorHandler
{
    /// <summary>
    /// Central error handling for WCF services.
    /// Whenever a service encounteres an unhandled exception, it will end up here.
    /// This method will log the error and continue with normal error processing.
    /// </summary>
    /// <param name="error">The unhandled exception thrown from a service method.</param>
    /// <returns>true if the exceptions has been handled, false if normal error processing should continue. This implementation will always return false.</returns>
    public virtual bool HandleError(Exception error)
    {
        return false; // returning false so that WCF will still process the exception as usual.
    }

    /// <summary>
    /// Create a custom Fault message to return to the client.
    /// </summary>
    /// <param name="error">The Exception object thrown in the course of the service operation.</param>
    /// <param name="version">The SOAP version of the message.</param>
    /// <param name="fault">The Message object that is returned to the client, or service, in the duplex case.</param>
    public virtual void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        //If it's a FaultException already, then we have nothing to do
        if (error is FaultException)
            return;

        // pack the exception info into the Fault Contract
        MyFaultContract details = new MyFaultContract();
        if (exception is NullReferenceException)
        {
            details.ErrorCode = MyFaultCode.NullReferenceException;
            details.OriginalMessage = error.Message;
            details.MyMessage = "Null Reference exception here!";
        }
        var faultException = new FaultException<MyFaultContract>(details);

        // turn the fault contract into the response WCF Message.
        var messageFault = faultException.CreateMessageFault();
        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
}

Конечно, если у вас есть другие причины хотеть использовать Ent Lib, тогда продолжайте. Вы, вероятно, можете как-то связать их обоих.

Эта ссылка также хорошо читается при обработке ошибок, включая добавление пользовательского поведения IErrorHandler: http://www.codeproject.com/KB/WCF/WCFErrorHandling.aspx

0 голосов
/ 09 января 2012

Я собирался сделать это комментарием, но он стал слишком длинным.

Рассматривали ли вы использование экранирования исключений блоков обработки исключений в обработчике исключений контракта отказа? Я думаю, что это делает то, что вы хотите. http://msdn.microsoft.com/en-us/library/ff953192(v=PandP.50).aspx

Подход заключается в создании собственного обработчика для сопоставления всех свойств с вашим пользовательским типом исключения. Затем настройте FaultContractExceptionHandler для сопоставления свойств в вашем исключении со свойствами в договоре об ошибках.

Если вам не нужно устанавливать значение для MyFaultCode, вы, вероятно, можете избежать написания собственного обработчика все вместе.

...