Category Archives: Best practices

Dlaczego nie zwracać typu IQueryable w repo?

clip_image001W ostatnim wpisie poruszyłem tematykę IoC i w przykładowym kawałku kodu dla LocationRepo znajduje się metoda GetLocations zwracająca typ IQueryable. Zwróciła na to uwagę pewna osoba odwiedzająca bloga i słusznie. Stwierdziłem więc, że lepiej będzie rozpisać się krótko na ten temat niż edytować poprzedni wpis. No więc dlaczego zwracanie typu IQueryable<T> jest złe? Można by powiedzieć, że zwracanie ogólnie jest złe i czasem się zdarza 😉 ale do rzeczy.

Implementując repozytorium powinniśmy tworzyć nową metodę dla każdego zapytania, np. GetLocationsByName, GetLocationsBySth, itd. Nie jest dobrą praktyką utworzenie jednej metody albo właściwości zwracającej całą kolekcję typu IQueryable<T>, po to by następnie filtrować elementy przy pomocy LINQ już poza repozytorium. Zapytania LINQ stosowane dla kolekcji IQueryable<T> są tłumaczone na drzewa wyrażeń, które następnie tłumaczone są na język określonego źródła danych, np. SQL. Zwracając z repozytorium taki typ, odpowiedzialność za tworzenie zapytań do źródła danych przechodzi na dewelopera implementującego kod w warstwie wyższej, warstwie domeny. W konsekwencji kod taki nie jest testowalny w 100%. Nie znamy wszystkich możliwych kombinacji zapytań, które mogą występować.
Kolejnym argumentem, który przemawia za niestosowaniem typu IQueryable w repozytoriach, jest to że uzależniamy nasze repozytorium od konieczności używania Entity Frameworka albo LINQ to SQL, ponieważ w przypadku innych źródeł danych musiałby być zaimplementowany mechanizm zwracania IQueryable. Chcemy aby nasz kod był uniwersalny i nie chcemy utrudniać sobie życia implementując dla każdego nowego źródła danych IQueryable, więc jedynym wyjściem jest unikanie tego typu. Oczywiście czasami może się on przydać, jednak musimy mieć to wszystko na uwadze 😉
Poniżej przykład metod zawartych w repozytorium.
Tak nie robimy

lepiej zróbmy to tak

Jak dodać kontener IoC?

Doctor with a syringe and patient. Isolated over white

Co to jest kontener IoC? IoC czyli Inversion of Control lub też odwrócenie zależności, brzmi bardzo ogólnie, ale co się pod tym kryje?
Możemy tutaj zaliczyć wzorzec fabryki, service locatora (lub też antywzorzec), czy też dependency injection i na tym ostatnim się skupimy.
Wzorzec ten może zostać zaimplementowany np. poprzez: constructor injection, property injection, jednak najbardziej sensowny jest ten pierwszy sposób.
Polega on na wstrzyknięciu zależności poprzez konstruktor. Kontener IoC ma nam w tym wszystkim pomóc, chodzi o to aby wstrzyknął on za nas jakiś konkretny typ, np. za typ ILocationRepo zostanie wstrzyknięty LocationRepo.

Po co nam to panie? No to zacznijmy od początku…
W przypadku web aplikacji ASP.NET MVC, domyślnie stworzony kontroler zawiera w sobie logikę, jednak dobrą praktyką jest wydzielenie warstwy logiki. W tym celu stworzyłem repozytoria w osobnym projekcie. Jednak aby wykorzystać dane repozytorium w kontrolerze, nie chcemy tworzyć instancji konkretnej klasy, chcemy być bardziej elastyczni i w razie potrzeby wstrzyknąć w to miejsce nową implementacje repozytorium. Do tego celu zastosujemy Dependency Injection i kontener IoC.

Pierwsza rzecz, która pozostaje do zrobienia to implementacja interfejsów dla każdego repozytorium oraz dla kontekstu. Tworzymy przykładowo interfejs dla repozytorium Location. Na razie zawiera on tylko metodę GetLocations(), jednak później będzie on zawierał więcej metod.

Implementujemy ten interfejs w klasie LocationRepo.

Widzimy tutaj także typ ITimePlannerContext – jest to interfejs naszego kontekstu, który także należy stworzyć, aby nie uzależniać się od konkretnego kontekstu w repozytoriach. Następnie możemy już przejść do kontrolera i stworzyć w nim konstruktor, który umożliwi nam constructor injection.

Ok, ale skąd wiadomo jaką implementację wstrzyknąć pod dany interfejs? Do tego posłuży nam kontener IoC.

Jak dodać kontener IoC?
Poniżej przedstawię użycie kontenera Unity, jednak rozpocząłem też zabawę z Autofac i chyba w późniejszym czasie skorzystam z niego, ale o tym może później. Gdyby ktoś był ciekawy jak wypadają inne kontenery w porównaniu, szukając w sieci trafiłem na ciekawy link.
Teraz czas przedstawić jak zastosować Unity. Otwieramy NuGet package managera i wyszukujemy Unity.Mvc, instalujemy w naszej web aplikacji.

clip_image001

clip_image002

Po zainstalowaniu paczki, do folderu App_Start w naszej aplikacji zostaną dodane dwie dodatkowe klasy: UnityConfig i UnityMvcActivator. Przechodzimy do klasy UnityConfig i przystępujemy do implementacji metody RegisterTypes, w której zdefiniujemy które klasy implementują określone interfejsy. Kod całej klasy wygląda następująco.

W metodzie RegisterTypes jak sama nazwa wskazuje, rejestrowane są typy i tak np. za typ ILocationRepo podstawiamy LocationRepo, itd. W przypadku gdy stworzymy nową wersję LocationRepo i będziemy chcieli z niej korzystać, to wystarczy tylko ją tutaj zarejestrować i to wszystko. Oczywiście gdyby ktoś chciał można też wykorzystać w tym miejscu refleksję i wtedy zwolni nas to z obowiązku rejestrowania każdego nowego typu. Teraz możemy uruchomić aplikację i sprawdzić efekt. Bardzo proste prawda? Za to ile korzyści 😉