Alternatywa dla Exception Handling Application Block

3

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:  		// log exception information
 19:  		_log.Error(exc);
 20:  
 21:  		// do not rethrow these exceptions
 22:  		if (exc is NotImportantException)
 23:  			return false;
 24:  
 25:  		// rethrow all other exceptions
 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.

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit
Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET i współorganizator konferencji Programistok. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook, YouTube.

3 Comments

  1. Spoko, kwestia nazewnictwa. jak metoda sie nazywa Handle i zwraca true lub false to raczej bym sie spodziewal innego dzialania niz u Ciebie.

    To handle to tak jakby rethrow czy nawet shouldrethrow.

    od jakiegos czasu stosuje metode typu: olej wszystkie wyjatki z przedzialu, jedynie nie zaloguj. i zakazdym razem jest to robione w kodzie. nie ma to byc konfigurowalne zewnatrz. zreszta zmiana taka by robilo rethrow zewnatrz moze spodowoac ze aplikacja sie wysypie w miejscu w ktorym nie powinna – to znacyz zwroci wyjatek ktory jest nie oczekiwany i to na przyklad przy metodzie indexOf kiedy zakladamy ze wyjatek moze wystapic.

    trzeba tylko uwazac by HandleExceptions nie impelemntowalo zbyt duzo modulow, ale zakladajac ze jest ich od 3 do 10 w rozwiazaniu to sie zabardzo nie rozrosnie, wrazie co grupowanie elementow w VS + partial class i da sie tym zarzadzac :)

    Gutek

  2. @dario-g:
    W czym nlog jest lepsze od log4net? Nie to zebym oba rozwiazania analizowal i potrafil powiedziec ‘w czym jest lepsze log4net od nlog’ – po prostu jestem ciekaw:). Po zastapieniu EntLiba przez log4net bylem oszolomiony mozliwosciami (wlasciwie dopiero wtedy logowanie nabralo sensu). Czy nlog ma jakis taki killer-feature?