Наверняка вы сталкивались с ситуацией, когда в случае ошибки в 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.
И все будет работать нормально.
Комментариев нет:
Отправить комментарий