Jakiś czas temu z dużego projektu nad którym obecnie pracuję zostały całkowicie usunięte i zaorane biblioteki Enterprise Library. I życie stało się prostsze. Wykorzystywaliśmy je jedynie do logowania i obsługi wyjątków i JAKOŚ trzeba było te funkcjonalności w systemie zachować. O ile w przypadku logowania wybór jest dość naturalny (wypasiony log4net), o tyle z wyjątkami nie było już tak "oczywiście".
Na szczęście dzięki własnej fasadzie na EHAB, o której pisałem kilka miesięcy temu, nie było konieczności zastępowania wielolinijkowych snippetów w całym kodzie źródłowym. Wystarczyła elegancka podmiana samej implementacji pod fasadą i bez praktycznie żadnych zmian "na zewnątrz" pozbyliśmy się rozpasionych i totalnie "niezarządzalnych" plików konfiguracyjnych EntLiba.
Założenie było dość proste: napisać funkcjonalnie równoważny EHABowi komponent, który nie wymaga nie wiadomo czego, a po prostu DZIAŁA. Konfigurowalna "z zewnątrz" obsługa wyjątków to moim zdaniem rozwiązanie dość dyskusyjne i - raczej średnio praktyczne. Dlatego też "nowe" polityki są definiowane wyłącznie w kodzie. Oto jak zmodyfikowane zostało przedstawione poprzednie rozwiązanie:
Każdy zbiór zasad musi implementować interfejs IExceptionHandlingPolicy:
1: public interface IExceptionHandlingPolicy
2: {
3: bool Handle(Exception exc);
4: }
Na przykład tak:
1: public class DataAccessPolicy : IExceptionHandlingPolicy
2: {
3: #region Singleton
4:
5: public static readonly IExceptionHandlingPolicyInstance = new DataAccessPolicy();
6:
7: private DataAccessPolicy()
8: {
9:
10: }
11:
12: #endregion
13:
14: private readonly ILog _log = LogManager.GetLogger(typeof(DataAccessPolicy));
15:
16: public bool Handle(Exception exc)
17: {
18:
19: _log.Error(exc);
20:
21:
22: if (exc is NotImportantException)
23: return false;
24:
25:
26: return true;
27: }
28: }
Dzięki takiej implementacji nadal mamy możliwość sterowania LOGOWANIEM informacji o wyjątku w KONFIGURACJI - a to dzięki log4net. Tam możemy wpisać czy informacja o danym wyjątku idzie do EventLoga, na konsolę, na maila czy jeszcze gdzieś indziej. I taka konfiguracja jest jak najbardziej OK, w przeciwieństwie do definiowania w zewnętrznym pliku takich reguł jak "czy wyrzucić wyjątek wyżej".
Powyższa Implementacja - jawne sprawdzanie typu wyjątku i decyzja na tej podstawie - może wydawać się głupia, naiwna i amatorska, ale moim zdaniem... nie ma w tym nic złego. Reguły takie tak czy siak nie powinny się zbytnio rozrastać - w końcu ile różnych typów wyjątków chcemy rozpoznawać na tym etapie na takim poziomie szczegółowości?
Wykorzystanie tak zdefiniowanych strategii wymagało jedynie niewielkiej modyfikacji istniejącej już fasady:
1: public static class HandleExceptions
2: {
3: public static void DataAccess(Action operation)
4: {
5: Handle(operation, DataAccessPolicy.Instance);
6: }
7:
8: public static T DataAccess<T>(Func<T> operation)
9: {
10: return Handle(operation, DataAccessPolicy.Instance);
11: }
12:
13: public static void DataAccess(Exception exception)
14: {
15: Handle(exception, DataAccessPolicy.Instance);
16: }
17:
18: /// <summary>
19: /// Performs a given function handling exceptions that might occur.
20: /// </summary>
21: /// <returns>Result of the operation or a default value for a given type.</returns>
22: private static T Handle<T>(Func<T> operation, IExceptionHandlingPolicy policy)
23: {
24: T ret = default(T);
25:
26: Handle(() =>
27: {
28: ret = operation();
29: }, policy);
30:
31: return ret;
32: }
33:
34: /// <summary>
35: /// Performs a given procedure handling exceptions that might occur.
36: /// </summary>
37: private static void Handle(Action operation, IExceptionHandlingPolicy policy)
38: {
39: try
40: {
41: operation();
42: }
43: catch (Exception exc)
44: {
45: if (policy.Handle(exc))
46: throw;
47: }
48: }
49:
50: /// <summary>
51: /// Handles a given exception.
52: /// </summary>
53: private static void Handle(Exception exception, IExceptionHandlingPolicy policy)
54: {
55: if (policy.Handle(exception))
56: throw exception;
57: }
58: }
Co o tym myślicie? Należy zdawać sobie sprawę z tego, że jawne opakowanie kodu w metodę HandleExceptions(...) występuje sporadycznie w kluczowych miejscach i MOŻE BYĆ traktowane jako aspekt... tyle że zaimplementowany domowym sposobem.