czwartek, 19 grudnia 2019

Rzucanie wyjątków wewnątrz bloku using

Jednym z zaleceń Microsoftu podczas implementacji interfejsu IDisposable jest aby implementowana metoda nie rzucała wyjątku https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1065-do-not-raise-exceptions-in-unexpected-locations?view=vs-2015&redirectedfrom=MSDN

Oczywiście zalecenia zaleceniami, a życie życiem :) Jednym z koronnych przykładów złamania tego zalecenia jest klasa ChannelFactory<TChannel> która pozwala na komunikację z webserwisem WCF. Jeżeli spojrzymy do źródeł tej klasy zobaczymy wiele miejsc, w których metoda Dispose rzuca wyjątkami związanymi np. z połączeniem do serwisu. Dlaczego jest to dla nas takie istotne? 
Aby uświadomić sobie problem z którym mamy tutaj do czynienia spójrzmy na kawałek przykładowego kodu który implementuje interfejs IDisposable

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ShowSomethingOnScreen();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }
        }

        private static void ShowSomethingOnScreen()
        {
            using (var dc = new DisposableClass())
            {
                dc.DoSomething();
                throw new InvalidOperationException("Using block");
            }
        }
    }

    public class DisposableClass : IDisposable
    {
        public void DoSomething()
        {
            Console.WriteLine("I'm doing some work!");
        }

        public void Dispose()
        {
            throw new InvalidOperationException("DisposeMethod");
        }
    }

Powyższy przykład rzuca dwa wyjątki:
  • jeden z wyjątków zdefiniowany jest w metodzie ShowSomethingOnScreen
  • rugi z wyjątków znajduje się w metodzie Dispose klasy DisposableClass
Zadajmy sobie pytanie: po uruchomieniu aplikacji, który z wyjątków zostanie przechwycony w metodzie Main i dlaczego? 
Na pierwszy rzut oka wydawałoby się, że zostanie przechwycony wyjątek z treścią "Using block". Stanie się jednak inaczej - zostanie 



Aby odpowiedź na pytanie dlaczego przechwycimy wyjątek z metody Dispose a nie z wewnątrz bloku using należy zobaczyć w jaki sposób kompilator przetłumaczy nasz kod. Blok using w rzeczywistości jest blokiem try finally:

 .method private hidebysig static 
  void ShowSomethingOnScreen () cil managed 
 {
  // Method begins at RVA 0x2084
  // Code size 37 (0x25)
  .maxstack 1
  .locals init (
   [0] class UsingKeyWord.DisposableClass dc
  )

  // (no C# code)
  IL_0000: nop
  // using (DisposableClass disposableClass = new DisposableClass())
  IL_0001: newobj instance void UsingKeyWord.DisposableClass::.ctor()
  // (no C# code)
  IL_0006: stloc.0
  .try
  {
   IL_0007: nop
   // disposableClass.DoSomething();
   IL_0008: ldloc.0
   IL_0009: callvirt instance void UsingKeyWord.DisposableClass::DoSomething()
   // (no C# code)
   IL_000e: nop
   // throw new InvalidOperationException("Using block");
   IL_000f: ldstr "Using block"
   IL_0014: newobj instance void [System.Runtime]System.InvalidOperationException::.ctor(string)
   // (no C# code)
   IL_0019: throw
  } // end .try
  finally
  {
   IL_001a: ldloc.0
   IL_001b: brfalse.s IL_0024

   IL_001d: ldloc.0
   IL_001e: callvirt instance void [System.Runtime]System.IDisposable::Dispose()
   IL_0023: nop

   IL_0024: endfinally
  } // end handler
 } // end of method Program::ShowSomethingOnScreen


Możemy więc rozpisać w jaki sposób zostanie przetworzony program:
  1. Program startuje od metody Main i wywołuje metodę ShowSomethingOnScreen w bliku try catch
  2. Otwarcie bolku using w metodzie ShowSomethingOnScreen 
  3. Wywołanie metody DoSomething która wypisuje zdanie w konsoli
  4. Rzucenie wyjątku throw new InvalidOperationException("Using block");
  5. Wywołanie bloku finally, który wywołuje metodę Dispose
  6. Metoda Dispose rzuca wyjątek
  7. Wyjątek rzucony w metodzie Dispose przekazywany jest do bloku catch metody Main
Teraz zostaje już tylko odpowiedzieć pytanie - jak prawidłowo obsłużyć oba wyjątki aby nie pominąć żadnego z nich?
Niestety, ale aby było możliwe przechwycenie obu wyjątków musimy porzucić blok using i skorzystać ze zwykłego bolku try catch finally:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ShowSomethingOnScreen();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }
        }

        private static void ShowSomethingOnScreen()
        {
            DisposableClass dc = null;
            try
            {
                dc = new DisposableClass();
                throw new InvalidOperationException("Using block");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                dc?.Dispose();
            }
        }
    }

    public class DisposableClass : IDisposable
    {
        public void DoSomething()
        {
            Console.WriteLine("I'm doing some work!");
        }

        public void Dispose()
        {
            throw new InvalidOperationException("DisposeMethod");
        }
    }


Wydawałoby się, że problem raczej niezbyt popularnych, jednak można z nim się spotkać i warto wiedzieć skąd bierze się jego źródło :)

sobota, 7 grudnia 2019

Dependency Injection - sposoby wstrzykiwania zależności

Dependency injection - po polsku wstrzykiwanie zależności. DI jest wzorcem projektowym opisującym sposoby przekazania zależności do obiektów.
Tradycyjne podejście tworzenia oprogramowania nie zakłada przekazywania zależności za pomocą DI, ale tworzenie obiektów gdy są potrzebne. Najczęściej podczas nauki programowania rozpoczynamy pisanie projektów tworząc obiekty, kiedy chcemy z nich skorzystać. DI to wzorzec, który ma na celu wyeliminowanie tworzenia obiektów ad-hoc do fabryk czy specjalistycznych kontenerów DI.

Ten post ma na celu omówienie sposobów realizacji wstrzykiwania zależności.

Wstrzykiwanie przez konstruktor (constructor injection)

Jest to najczęściej wykorzystywana metoda przekazywania zależności do klasy. Całość opiera się o zastosowanie jednego konstruktora, który przyjmuje wszystkie zależności klasy. Przykład:

    public class Mapper
    {
        private readonly IAddressMapper _addressMapper;
        private readonly IPhoneMapper _phoneMapper;
        private readonly IEmailMapper _emailMapper;

        public Mapper(IAddressMapper addressMapper, IPhoneMapper phoneMapper, IEmailMapper emailMapper)
        {
            _addressMapper = addressMapper ?? throw new ArgumentNullException(nameof(addressMapper));
            _phoneMapper = phoneMapper ?? throw new ArgumentNullException(nameof(phoneMapper));
            _emailMapper = emailMapper ?? throw new ArgumentNullException(nameof(emailMapper));
        }
    }

Kod możemy podzielić na sekcje:
  • prywatne pola, przechowujące referencje do implementacji interfejsów (należy podkreślić, że klasa Mapper nie zna szczegółów implementacji wstrzykiwanych zależności). Pola dodatkowo oznaczone są jako readonly co zapewnia nas, że raz zainicjowane nie zostaną w trakcie wykonywania programu zmienione. 
  • publiczny konstruktor, który przypisuje referencję do zależności w celu ich późniejszego wykorzystania
  • Guard Clause - inaczej znaną także jako null check validation - sprawdzamy, czy wstrzykiwane zależności nie są wartościami null
Zalety i wady tego sposobu wstrzykiwania zależności:
+ dokumentacja klasy - widząc jakie zależności są wstrzykiwane, wiemy czego możemy się po danej klasie spodziewać
+ sprawdza się zarówno z kontenerami DI jak i bez nich
+ stan pól (zależności) klasy jest zawsze prawidłowy po utworzeniu obiektu (zależności nie są nullami)
+ podwójna ochrona: kompilator pilnuje aby wszystkie zależności zostały podane podczas tworzenia klasy, Guard Clause sprawdza aby nie były nullami
- jeżeli klasa wymaga zbyt dużej ilości zależności należy rozważyć czy kodu nie należy przeorganizować. Oczywiście nie ma górnego ani dolnego limitu ilości zależności - wszystko zależy od konkretnego przypadku
- jeżeli klasa ma być serializowana, będzie potrzebny domyślny konstruktor
- nie każda metoda wymaga wszystkich zależności - w takim wypadku należy zastanowić się czy aby na pewno architektura klasy jest prawidłowa i czy część kodu nie powinna zostać wydzielona do osobnej klasy/biblioteki

Mimo wszystkich zalet i wad jest to najczęściej polecana technika wstrzykiwania zależności i najłatwiejsza do implementacji za pomocą dostępnych kontenerów. 

Wstrzykiwanie przez właściwość (property injection)

Drugi sposób wstrzykiwania zależności to wstrzykiwanie przez właściwość. Inne nazwy, które można spotkać to: property injection, setter injection. Sposób ten zalecany jest gdy istnieje domyślna implementacja, czyli inaczej mówiąc zależność jest opcjonalna. Jeżeli nie istnieje dobra implementacja domyślna, zalecane jest wykorzystanie wstrzykiwania przez konstruktor. 
Zobaczmy na przykład:

    public class Report
    {
        private IFormat reportFormatter;
        public IFormat ReportFormatter
        {
            get
            { return reportFormatter ?? (ReportFormatter = new TxtFormatter()); }
            set
            {
                if (value == null) throw new ArgumentNullException(nameof(value));
                if (reportFormatter != null) throw new InvalidOperationException();
                reportFormatter = value;
            }
        }

        public void CreateReport()
        {
            //some code that generates report data
            var reportData = new ReportData { Name = "Average salary in company", CreateDate = DateTime.Now, NumberOfEmployee = 1000, AverageSalary = 5000 };
            var report = ReportFormatter.Convert(reportData);
            //rest of code, for example saving to file
        }
    }

    public interface IFormat
    {
        string Convert(ReportData reportData);
    }

    public class TxtFormatter : IFormat
    {
        public string Convert(ReportData reportData)
        {
            var reportTextBuilder = new StringBuilder();
            reportTextBuilder.AppendLine($"Report name: {reportData.Name}");
            reportTextBuilder.AppendLine($"Create date: {reportData.CreateDate}");
            reportTextBuilder.AppendLine($"Number of employees in company: {reportData.NumberOfEmployee}");
            reportTextBuilder.AppendLine($"Average salary in company: {reportData.AverageSalary}");
            return reportTextBuilder.ToString();
        }
    }

    public class ReportData
    {
        public string Name { get; set; }
        public DateTime CreateDate { get; set; }
        public int NumberOfEmployee { get; set; }
        public decimal AverageSalary { get; set; }
    }

Powyżej mamy do czynienia z klasą tworzącą raport. Raport chcemy przed zapisem skonwertować do zadanego formatu. Domyślną implementacją jest zwykły tekst. Property przyjmujące zależność formatującą dane raportu możemy podzielić na następujące części:
  • getter - jeżeli formatter istnieje - zwraca go, w przeciwnym razie podstawia domyślną implementację
  • setter:
    • sprawdzamy czy przesłana wartość nie jest nullem. Podobnie jak w przypadku wstrzykiwania przez konstruktor chcemy uchronić się przed złym stanem obiektu.
    • sprawdzamy czy już wcześniej zależność nie została zainicjowana. Nie chcemy pozwolić aby podczas pracy z klasą w różnych miejscach była inicjowana w różny sposób co mogłoby skutkować niestabilnym czy też niezrozumiałym zachowaniem aplikacji
    • jeżeli powyższe warunki nie mają miejsca zwracana jest zainicjowana zależność

Zalety i wady wstrzykiwania przez właściwości:
+ zależność może zostać zmieniona podczas działania programu
+ elastyczność
- obiekt może mieć nieprawidłowy stan
- większy narzut kodu aby kod był odporny na błędy programistyczne 
- mniej intuicyjne

Wstrzykiwanie przez metodę

Kiedy zależność zmienia się dla każdego wywołania metody, możemy ją przesłać jako argument wywołania metody. 
W poprzednich typach wstrzykiwania zależności (przez konstruktor, przez właściwość) była ona dostarczana podczas budowania drzewa zależności. Wstrzykiwanie przez metodę pozwala dostarczyć zależność do już istniejącego obiektu.
Technika ta szczególnie może być przydatna podczas implementacji encji (Entity) w podejściu DDD (Domain-driven design). 
Spójrzmy na przykład:

    public class Engine
    {
        public int Accelerate(int fuelValue, ITurboEngine turboEngine)
        {
            if (turboEngine == null)
            {
                throw new ArgumentNullException(nameof(turboEngine));
            }

            return turboEngine.IsTurboEngine() ? (int) (value * 1.2) : value;
        }
    }

Klasa Engine posiada metodę Accelerate która na podstawie wartości wtryskiwanego paliwa zwraca o ile samochód przyśpieszy (oczywiście w realnym świecie obliczenie wartości przyspieszenia jest znacznie bardziej skomplikowane, tutaj tylko prosty przykład :)). Interfejs ITurboEngine posiada metodę determinującą czy silnik posiada turbodoładowanie. Jeżeli mamy do czynienia z silnikiem turbodoładowanym osiągnięte przyśpieszenie będzie większe. 
Powyższy przykład pokazuje w jaki sposób możemy wstrzyknąć zależność poprzez metodę. Najczęściej będziemy mieli do czynienia z konstrukcją w której przekażemy konkretną wartość dla metody oraz serwis - czyli kontekst wykonania (zależność). 

Zalety i wady tej techniki:
+ pozwala wstrzyknąć zależność w odniesieniu do kontekstu wywołania metody
+ zależności można wstrzykiwać poza Composition Root
- małe spektrum zastosowań
- zależność staje się publiczną częścią API klasy