Projektanci WCF zostawili w swoim produkcie bardzo dużo półprzymkniętych furtek czekających tylko na odkrycie i wykorzystanie ich potencjału. Jednym z takich czarodziejskich otworów do przyjemnego, różowego, miękkiego wnętrza WCF jest infrastruktura Behaviors (więcej o, między innymi, nich, na przykład w artykule “Extending WCF with Custom Behaviors“). Z ich pomocą można osiągnąć baaardzo wiele, między innymi: w niesamowicie elegancki sposób zebrać w jednym miejscu wszystkie błędy wygenerowane przez naszą warstwę usług. Wystarczy zaimplementować interfejs IErrorHandler… i wsio! Ja połączyłem implementację dwóch interfejsów w jednej klasie tak, aby całą logikę odpowiedzialną za umieszczanie “łapacza wyjątków” zawrzeć obok siebie. I wygląda to tak:
1: public class ErrorHandlingBehavior : IServiceBehavior, IErrorHandler 2: { 3: public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 4: { 5: var faultExc = new FaultException(error.Message); 6: var messageFault = faultExc.CreateMessageFault(); 7: fault = Message.CreateMessage(version, messageFault, faultExc.Action); 8: } 9: 10: public bool HandleError(Exception error) 11: { 12: EventLog.WriteEntry("ProcentSampleWcfApp", error.ToString(), EventLogEntryType.Error); 13: return false; 14: } 15: 16: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 17: { 18: } 19: 20: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) 21: { 22: } 23: 24: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 25: { 26: foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) 27: { 28: channelDispatcher.ErrorHandlers.Add(this); 29: } 30: } 31: }
Dwie pierwsze metody odpowiedzialne są za czynności związane z wyjątkami. Pierwsza, ProvideFault(…), ma za zadanie podmienić faktyczny wyjątek na strukturę, którą życzymy sobie wysłać do klienta. W tym przypadku jest to obudowanie odpowiednich informacji w wyjątek WCF, FaultException… a implementacja zerżnięta z książki “Programming WCF Services“. Metoda ta powinna być jak najbardziej wydajna, ponieważ wykonywana jest synchronicznie. Inaczej sprawa się przedstawia w przypadku drugiej dzidzi, HandleError(…). To maleństwo z kolei ma za zadanie zrobić z wyjątkiem COKOLWIEK, w domyśle: zalogować. W powyższej implementacji wrzucam informację o błędzie do event logu.
Pozostałe metody to (w większości pusta) implementacja interfejsu definiującego zachowanie usługi. O tym rozpisywać się nie będę, zainteresowani bez problemu znajdą szczegółowy opis możliwych do wykonania czynności w internecie.
Zaaplikowanie takiego mechanizmu do naszej usługi wygląda w kodzie tak:
1: host.Description.Behaviors.Add(new ErrorHandlingBehavior()); 2: host.Open();
I… to tyle. Mamy również możliwość stworzenia własnego atrybutu o identycznej implementacji i aplikacji go bezpośrednio na klasę usługi, efekt będzie ten sam.
Na koniec wspomnieć należy o banalnej wręcz integracji EntLibowego bloku EHAB z WCF. Jeśli korzystasz z tego mechanizmu (serio?) to polecam zapoznanie się z artykułem “Shielding Exceptions at WCF Service Boundaries” na MSDN.