Dependency Injection - jak prawidłowo użyć

0

Witam ponownie.

Tworzę oprogramowanie typu "cms". Mam projekt podzielony na trzy części:

  1. Login - zawiera logikę: repozytoria, fasady etc.
  2. Data - zawiera konteksty entity framework oraz klasy modelu, encje etc.
  3. Web - aplikacja ASP.NET MVC
  4. Test - zawiera testy do powyższych

Aktualnie jestem na etapie tworzenia repozytorium i zastanawiam się nad dalszym użyciem.

Repozytorium posiada abstrakcyjną klasę generyczną, która przyjmuje jako argument konstrutkora obiekt IContext, który odpowiednio udostępnia DbSet itp.
Czemu to tak robię? Ponieważ w testach podstawiam sobie zmockowany DbContext. Teraz moje pytanie jak tego użyć w aplikacji Web?
Aplikacja Web nie ma referencji do Data - nie chcę by miała bezpośredni dostęp do kontekstów, a jeśli już to chciałbym te elementy utworzyć w jakimś kontenerze IoC tak, żeby programista w Web nie pisał takiego kodu

 
var repository = new FileRepository(new FileContext());

idealnie by było

var repository = new FileRepository();

Możliwe jest również bym w klasie miał coś takiego:

 
class CosTam{
 private IRepository _fileRepository;
 public CosTam(IRepository fileRepository){
_fileRepostiry =fileRepository;
}
}

?? czy coś w tym stylu.

Troszkę nagmatwane, ale będę wdzięczny z aporady. Dopiero zaczynam ogarniać projekt z użyciem wzorców projektowych

1
StefanWciążMaProblem napisał(a):
  1. Login - zawiera logikę: repozytoria, fasady etc.

Ciekawa nazwa.

Aplikacja Web nie ma referencji do Data - nie chcę by miała bezpośredni dostęp do kontekstów, a jeśli już to chciałbym te elementy utworzyć w jakimś kontenerze IoC tak, żeby programista w Web nie pisał takiego kodu

 
var repository = new FileRepository(new FileContext());

idealnie by było

var repository = new FileRepository();

Jeśli chcesz użyć jakiegoś kontenera IoC, to programista w ogóle nie będzie używał żadnego new, tylko wszystkie repozytoria zostaną wstrzyknięte do kontrolerów (fuj). Cały problem, to kwestia konfiguracji kontenera, ale jest to wykonalne, bo większość z nich pozwala na taki mechanizm, że każdy projekt ma swoją klasę konfigurującą kontener, a kontener podczas uruchomiania aplikacji czesze wszystkie binarki i się konfiguruje.

0
somekind napisał(a):
StefanWciążMaProblem napisał(a):
  1. Login - zawiera logikę: repozytoria, fasady etc.

Ciekawa nazwa.

Logic *

somekind napisał(a):

Jeśli chcesz użyć jakiegoś kontenera IoC, to programista w ogóle nie będzie używał żadnego new, tylko wszystkie repozytoria zostaną wstrzyknięte do kontrolerów (fuj). Cały problem, to kwestia konfiguracji kontenera, ale jest to wykonalne, bo większość z nich pozwala na taki mechanizm, że każdy projekt ma swoją klasę konfigurującą kontener, a kontener podczas uruchomiania aplikacji czesze wszystkie binarki i się konfiguruje.

Co złego ze wstrzykiwaniem repozytoriów do kontrolerów? W takim razie jak powinno to być zaprojektowane z użyciem DI?

0
stefansmutny napisał(a):

Co złego ze wstrzykiwaniem repozytoriów do kontrolerów? W takim razie jak powinno to być zaprojektowane z użyciem DI?

Wstrzykiwanie jest dobre, to repozytoria w kontrolerach są moim zdaniem złe. Kontroler to logika prezentacji, repozytorium to logika biznesowa, i tego nie powinno się mieszać.

0

Repozytoria powinny byc wstrzykiwane do fasad, a fasady powinny byc wstrzykiwane do kontrolerow.

0
ghfdgdf napisał(a):

Repozytoria powinny byc wstrzykiwane do fasad, a fasady powinny byc wstrzykiwane do kontrolerow.

Jakich fasad? Fasady są do zewnętrznych systemów, nie do logiki biznesowej własnej aplikacji. Od tego są serwisy aplikacyjne.

0

Serwis aplikacyjny to rodzaj fasady, mysle ze autor posta piszac o fasadach mial na mysli serwery aplikacyjne

0

Teraz Stefan jest bardzo smutny.
Nie wiem juz zupełnie jak tworzyć komunikować się logiką prezentacji z logiką biznesową?

Jeśli serwis to, gdzie taki serwis powinien być zaimplementowany? W oddzielnym projekcie o anzwie infrastruktura czy co.
:/

1
gfgdd napisał(a):

Serwis aplikacyjny to rodzaj fasady, mysle ze autor posta piszac o fasadach mial na mysli serwery aplikacyjne

No jeśli tak na to patrzysz to ok, chociaż ja lubię używać bardziej ścisłych nazw. ;)

stefanZdezorientowan napisał(a):

Teraz Stefan jest bardzo smutny.
Nie wiem juz zupełnie jak tworzyć komunikować się logiką prezentacji z logiką biznesową?

Kontroler ma pole (albo wiele) typu IJakiśApplicationService, które jest wstrzykiwane przez kontener IoC.
Implementacja tego serwisu zawiera pola typu ICośtamRepository (albo wiele), które też są wstrzykiwane przez kontener IoC.
Kontroler to warstwa prezentacji, serwis to warstwa dająca mu dostęp do logiki biznesowej. Kontroler z serwisem komunikuje się za pomocą obiektów reprezentujących żądania zrobienia czegoś, które są właściwie prostymi DTO. Serwis zwraca mu również DTO, które często mogą być gotowymi ViewModelami.

Jeśli serwis to, gdzie taki serwis powinien być zaimplementowany? W oddzielnym projekcie o anzwie infrastruktura czy co.
:/

W Twoim przypadku umieściłbym je w projekcie Logic, żeby się niepotrzebnie nie rozdrabniać. Infrastruktura to rzeczy typu parser/builder plików CSV, logger, różne helpery, a nie logika aplikacji.

0

Ok dzięki wielkie.
A powiedz mi jeszcze jak mogę rozwiązać problem IoC dla projektu Logic? Jest to projekt typu dll, więc nie ma punktu wejściowego -> nie ma miejsca gdzie mogę odpalić Bootstrapp i skonfigurować kontener

2

Przecież "punkt wejściowy" definiowany jest dla aplikacji, a nie projektu.
Jezeli masz MVC to w Global.asax.cs wywolujesz metode konfigurujaca kontener.
A wszystkie zaleznosci beda same sie rozwiazywac poczawszy od 'korzenia' (projekt MVC) po pozostale warstwy.

0

Odkopię trochę wątek, @somekind:

Kontroler z serwisem komunikuje się za pomocą obiektów reprezentujących żądania zrobienia czegoś, które są właściwie prostymi DTO. Serwis zwraca mu również DTO, które często mogą być gotowymi ViewModelami.

Jak wygląda wtedy kwestia encja <=> DTO? Używasz do tego jakiś mapperów? I później w ViewModelach bezpośrednio DTO, czy jednak kolejna konwersja na ViewModel? Czy encje różnią się czymś więcej niż polami i ewentualnymi atrybutami? Kiedyś spotkałem się z praktyką, że z serwisu zamiast JakisObiektDTO zwraca się generyczny ServiceResult, który ma pole Result będące obiektem np. JakisObiektDTO oraz pole IsError, które informuje kontroler, czy service wykonał operację poprawnie - ktoś z czymś takim miał do czynienia?

1
pytajacy napisał(a):

Jak wygląda wtedy kwestia encja <=> DTO? Używasz do tego jakiś mapperów?

Oczywiście, przecież nie będę przepisywał wartości ręcznie.

I później w ViewModelach bezpośrednio DTO, czy jednak kolejna konwersja na ViewModel?

To zależy, bo czasami ViewModele muszą być bardziej skomplikowane niż DTO, być jakąś ich kompozycją, itp.
Jeśli aplikacja jest mała i nie ma pośredniej warstwy fizycznej, to w ogóle pośrednie DTO nie mają sensu i encje lepiej mapować od razu na ViewModele.

Czy encje różnią się czymś więcej niż polami i ewentualnymi atrybutami?

Encja może zawierać jakieś dodatkowe metadane (np. audytowymi), jest mapowana na tabelę, więc może być też przyozdobiona różnymi atrybutami, które nie są potrzebne po stronie widoku. No, a przede wszystkim, encja zawiera metody logiki biznesowej, a ViewModele praktycznie żadnej logiki nie mają.

Kiedyś spotkałem się z praktyką, że z serwisu zamiast JakisObiektDTO zwraca się generyczny ServiceResult, który ma pole Result będące obiektem np. JakisObiektDTO oraz pole IsError, które informuje kontroler, czy service wykonał operację poprawnie - ktoś z czymś takim miał do czynienia?

Owszem, tak się robi zwłaszcza, gdy środkowa warstwa systemu jest oddzielną aplikacją (czyli najczęściej webserwisem). Jakoś przecież trzeba przesłać informacje o błędzie z warstwy biznesowej do warstwy prezentacji, najprościej właśnie opakowując w taki obiekt.

1 użytkowników online, w tym zalogowanych: 0, gości: 1