Ten artykuł pochodzi z serii przygotowań do egzaminu 70-503: Windows Communication Foundation.
Dzisiaj dowiemy się jak obsługiwać wyjątki serwisu po stronie klienta. WCF sygnalizuje wyjątki wykorzystując błędy SOAP. Wyróżniamy dwa typy błędów które mogą być wykorzystane: błędy typowane/deklarowane (ang. typed faults/declared SOAP faults) i beztypowe/niedeklarowane (ang. untyped faults/nondeclared SOAP faults).
Odbieranie błędów
Teoretycznie podczas korzystania z WCF’a możliwie jest występowanie wielu różnych wyjątków, ale najczęściej występują tylko dwa typy wyjątków: TimeoutException – występujący gdy zostanie przekroczony któryś z parametrów czasowych, oraz CommunicationException – występujący gdy zostanie wykryty “naprawialny” błąd zarówno w serwisie jak i w kliencie.
Z wyjątków dziedziczących po CommunicationException warto wspomnieć o typowanym jak i nietypowanym wyjątku FaultException. Różnica między nimi jest taka że wyjątek typowany FaultException<TDetail> jest wyrzucany gdy klient spodziewa się wyjątku (jest zadeklarowany w kontrakcie), a wyjątek nietypowany FaultException jest wyrzucany gdy klient na podstawie kontraktu nie spodziewa się wyjątków.
Deklarowane błędy SOAP
Do deklarowania błędów służy atrybut FaultContract, którym dekoruje się operacje mogące powodować błędy. Jako parametr podajemy klasę zawierającą informacje o błędzie. W ten sposób klient zanim wywoła jakąkolwiek operację wie jakich wyjątków może się spodziewać. Poniżej przykład zadeklarowanego błędu:
1: [ServiceContract()]
2: public interface IFaultService
3: {
4: [OperationContract()]
5: [FaultContract(typeof(DemoFault))]
6: string Hello(string name);
7: }
8:
9: [DataContract()]
10: public class DemoFault
11: {
12: [DataMember()]
13: public string ErrorText;
14:
15: public DemoFault(string errorMessage)
16: {
17: ErrorText = errorMessage;
18: }
19: }
Oznaczyliśmy atrybutem FaultContract (linia 5) metodę Hello. Dalej (od linii 9) mamy deklarację samego błędu (tutaj we właściwości ErrorText będzie zawarty komunikat o błędzie.
Od strony klienta obsługa tego błędu to zwykła obsługa wyjątku, przy czym właściwość Detail zawiera nasz obiekt klasy DemoFault.
1: try
2: {
3: FaultServiceClient proxy = new FaultServiceClient();
4: Console.WriteLine(proxy.Hello("World"));
5: }
6: catch (FaultException<DemoFault> helloFault)
7: {
8: Console.WriteLine(hellofault.Detail.ErrorText);
9: }
Na chwile zatrzymajmy się jeszcze przy ogólnej obsłudze wyjątków. Popatrzmy na poniższy przykład:
1: try
2: {
3: // Processing
4: }
5: catch (Exception ex)
6: {
7: Console.WriteLine(ex.ToString());
8: }
Mimo że wygląda to zupełnie poprawnie, to na konsoli nie zobaczymy komunikatu z klasy
DemoFault, zamiast tego sposobu lepiej odwoływać się bezpośrednio do właściwości
Detail wyjątku (
Console.WriteLine(ex.Detail)).
Niedeklarowane błędy SOAP
Tak jak pewnie się domyślacie, tego typu błędy nie zawierają konkretnego typu informacji o błędzie. Za to (tak jak każde inne typy wyjątków zawierają właściwość Message). Obsługa takich wyjątków wygląda tak:
1: try
2: {
3: FaultServiceClient proxy = new FaultServiceClient();
4: Console.WriteLine(proxy.Hello("World"));
5: }
6: catch (FaultException fe)
7: {
8: Console.WriteLine(fe.Message);
9: }
Na koniec jeszcze ważna uwaga. Przy obsłudze takich wyjątków ważna jest kolejność obsługi poszczególnych typów z zachowaniem hierarchii dziedziczenia. Poniżej przykład poprawnej kolejności:
1: try
2: {
3: FaultServiceClient proxy = new FaultServiceClient();
4: Console.WriteLine(proxy.Hello("World"));
5: }
6: catch (FaultException<DemoFault> de)
7: {
8: Console.WriteLine(de.Detail.ErrorText);
9: }
10: catch (FaultException fe)
11: {
12: Console.WriteLine(fe.Message);
13: }
14: catch (CommunicationException ce)
15: {
16: Console.WriteLine(ce.Message);
17: }
W następnej lekcji dowiemy się o co nieco o obsłudze sesji.