Бросок FaultException с использованием обработчика пользовательских исключений EL WCF
Поэтому я пытаюсь использовать 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 обработчика.
Первый - это пользовательский обработчик, который перехватывает исключение NullReference. Затем создается новое исключение - MyApplicationException с полем MyFaultContract.
Затем я настроил второй обработчик - встроенный "обработчик исключений Fault Contract", который перехватывает MyApplicationException, создает новое FaultException и автоматически сопоставляет MyFaultcontract из MyApplicationException с вновь созданным FaultException.
Клиент WCF по-прежнему перехватывает общее исключение FaultException, а не пользовательское соглашение.
2 ответа
Я хотел бы рассмотреть возможность использования пользовательской реализации IErrorHandler WCF. Он предоставляет возможность централизованно обрабатывать все необработанные исключения, возникающие в ваших методах обслуживания.
По моему мнению, это в конечном итоге чище, чем при использовании 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
Я собирался сделать это комментарий, но это слишком долго.
Рассматривали ли вы использование защиты от исключений для блоков обработки исключений в обработчике исключений контракта отказа? Я думаю, что это делает то, что вы хотите. http://msdn.microsoft.com/en-us/library/ff953192(v=PandP.50).aspx
Подход заключается в создании собственного обработчика для сопоставления всех свойств с вашим пользовательским типом исключения. Затем настройте Fault Contract Exception Handler для сопоставления свойств в вашем исключении со свойствами в договоре об ошибках.
Если вам не нужно устанавливать значение для MyFaultCode, вы можете избежать написания собственного обработчика.