Наверняка вы сталкивались с ситуацией, когда в случае ошибки в WCF-сервисе нужно передать клиенту какие-либо данные либо, например, подробное описание что случилось. Очень часто встречаются реализации WCF- сервисов, использующие возвращаемые клиенту объекты с дополнительными, используемыми только в случае ошибки, полями, даже если реализация метода не требует возврата какого-либо результата клиенту. Так делают потому, что если просто выбросить исключение, то клиент не получит практически никакой информации о том, что случилось. Представьте, что у нас есть WCF сервис вот с таким простым контрактом и реализацией.
//контракт
[ServiceContract]
public interface IDataService
{
[OperationContract]
void TestError();
}
//реализация
public void TestError()
{
throw new Exception("Error!");
}
Можно, конечно, как написано в ошибке, включить IncludeExceptionDetailInFaults, как написано в сообщении об ошибке. Но это свойство не рекомендуется использовать кроме как для отладки, так как при этом любой клиент в случае ошибки получить все данные о стеке вызовов и структуре вашего сервиса, что очень нехорошо с точки зрения безопасности.
Но, помимо варианта для отладки и архитектурно не самого лучшего варианта с дополнительным полем в результате вызова, есть и третий вариант - более удобный и более правильный.
Это использования FaultContract и FaultException. Давайте сделаем контракт и реализацию нашего сервиса с использованием этих возможностей WCF.
[ServiceContract]
//контракт
public interface IDataService
{
[OperationContract]
[FaultContract(typeof(ServiceError))]
void TestError();
}
[DataContract]
public class ServiceError
{
[DataMember]
public int ErrorCode { get; set; }
[DataMember]
public string Message { get; set; }
}
//реализация
public void TestError()
{
ServiceError srvError = new ServiceError();
srvError.ErrorCode = 123;
srvError.Message = "Error!";
throw new FaultException<ServiceError>(srvError);
}
Теперь, если ловить на клиенте не просто исключение а FaultException<>, то можно получить все те сведения, которые мы передали в сервисе.
using (TestService.TestServiceClient client = new TestService.TestServiceClient())
{
try
{
client.TestError();
}
catch (FaultException<TestService.ServiceError> ex)
{
//поля вашего класса, объявленного как FaultContract, будут находится в свойстве исключения Datail
//в данном случае появится сообщение "123Error!";
MessageBox.Show(ex.Detail.ErrorCode.ToString()+ex.Detail.Message);
}
}
В принципе, все должно работать нормально, за одним исключением - при отладке в Visual Studio у вас может появляться странная ошибка System.ServiceModel.FaultException`1 was unhandled by user code на то месте в сервисе, где вы выбрасываете исключение:
Одни советы в интернете говорят, что чтобы починить это надо полностью отключить Exception assistant (после чего, вообще никакие ошибки не будет ловится студией), другие что в настройках этого самого ассистента вообще отключить все CLR Exceptions (что, в контексте разработки на .Net равнозначно предыдущему варианту). На самом деле, достаточно отключить одно это исключение и все будет работать. Идем в Debug -> Exception, открываем категорию Common Language Runtime Exceptions, затем System.ServiceModel и убираем галочку напротив System.ServiceModel.FaultException`1.
И все будет работать нормально.



Комментариев нет:
Отправить комментарий