Dependency Injection z Unit Of Work

0

Cześć czy używając w projekcie UoW oraz Dependency Injection powinienem wstrzykiwać do kontrolera cały obiekt UnitOfWork a potem go przekazywać do serwisów zamiast pojedynczych repozytoriów? Może lepszym rozwiązaniem jest wstrzyknięcie UoW do konstruktora kontrolera i przypisanie go do pola prywatnego natomiast w akcji przekazanie już tylko danego repozytorium do serwisu. W takim wypadku wywołanie zapisu musiałoby się odbywać z poziomu akcji, a nie w serwisie - co jest chyba średnim rozwiązaniem.

Co o tym myślicie?

1

ani jedno ani drugie

wstrzykuj klase ktora zarzadza daną funkcjonalnoscia do kontrolera. Ta klasa niech posiada w konstruktorze UOW od DI i tam decydujesz kiedy zapisac zmiany z UOW i z jakich repozytoriow korzystac.

Kontroller nie ma nic zapisywac ani nie ma wiedziec nic o logice biznesowej

0

Kluczowym powodem implementacji UOW jest decydowanie o czynnościach związanych z zatwierdzaniem transakcji jak problem współbieżności. Poza tym są trzy podstawowe sposoby implementacji UOW ty prawdopodobnie mówisz o "Unit Of Work Controller" w najgłupszej postaci, czyli mutancie, który jest nakładką na EF i "repositories" (architektura z MS albo pakiet wzorców Dr. Freemana ma się rozumieć). Niektórzy robią sobie takiego wrapa na sesje ORM'a i ma to sens, jeśli chcesz używać repository w wariancie "Repository typu kolekcja" zamiast "trwały magazyn". Ty prawdopodobnie potrzebujesz tylko serwisu, który operuje na ORM jako taki Data Gate czy DAO. Controller to zupełnie inna bajka to służy do czego innego niż serwisy - fasady z niższego poziomu.

0
fasadin napisał(a):

ani jedno ani drugie

wstrzykuj klase ktora zarzadza daną funkcjonalnoscia do kontrolera. Ta klasa niech posiada w konstruktorze UOW od DI i tam decydujesz kiedy zapisac zmiany z UOW i z jakich repozytoriow korzystac.

Kontroller nie ma nic zapisywac ani nie ma wiedziec nic o logice biznesowej

Czy można liczyć na podesłanie jakiegoś przykładu z taki kodem? Może jakiś projekt na githubie.

0
._. napisał(a):

Co jest powodem implementacji twojego UOW, a raczej co ona zawiera?

Piszę projekt wolnym czasie w którym chciałbym wykorzystać jak najwięcej wzorców tak by struktura była dobrze zaprojektowana, oraz żeby nauczyć się jak najwięcej.

Wiem że toczy się dyskusja czy repozytorium jest w ogóle potrzebne i czy jest sens używania UoW.
W projekcie który piszę (nie będzie on jakiś skomplikowany) pewnie nie ma konieczności używania tych wzorców, chociaż z doświadczenia powiem że wolę mieć jasno wyznaczone miejsce w którym zdefiniowane są wszystkie zapytania do bazy, a nie rozwalone po całej aplikacji poprzez dobijanie się do DbContextu i tworzenie po parę razy tych samych zapytań. Nigdy wcześniej nie używałem UoW(w pracy problem był rozwiązywany przez DbContext.Save()).

Po przeczytaniu paru artykułów zdecydowałem się na użycie niegenerycznego repozytorium oraz UoW.

Tutaj zawartość moich klas:

    public interface IUnitOfWork : IDisposable
    {
        IUserRepository Users { get; }
        IRequestRepository Requests { get; }
        ITimeSlotRepository TimeSlots {get; }
        void Complete();
        Task CompleteAsync();
    }
 public class UnitOfWork : IUnitOfWork
    {
        private readonly ManageItDbContext _context;

        public UnitOfWork(ManageItDbContext context)
        {
            _context = context;
            Users = new UserRepository(_context);
            Requests = new RequestRepository(_context);
            TimeSlots = new TimeSlotRepository(_context);
        }

	//zawartość powinna mieć wszystkie repozytoria

        public IUserRepository Users { get; private set; }
        public IRequestRepository Requests { get; private set; }
        public ITimeSlotRepository TimeSlots { get; private set; }
        
        public void Complete()
        {
            _context.SaveChanges();
        }

        public async Task CompleteAsync()
        {
            await _context.SaveChangesAsync();
        }

        public void Dispose()
        {
            _context.Dispose();
        }
    }

Wszystko układało się chyba dobrze do momentu gdy zacząłem czytać o DI, z którym w pracy nie miałem styczności. Także nie wiem czy do każdej akcji w kontrolerze powinienem tworzyć osobny model i wywoływać jego nowy obiekt a wewnątrz dopiero za pomocą DI wstrzykiwać UoW? - może źle zrozumiałem poprzedniego posta.

0

Controller zawiera Manager'a

Manager zawiera UnitOfWork

w controlerze wywolujesz

manager.FeatureA()

w managerze

public async Task FeatureA() 
{
  var repositoryA = this.unitOfWork.RepositoryA;
  
  var result = repositoryA,GimmeAll();

  await result.DoSomethingElse(); //  nie chcialo mi sie pisac logiki biznesowej, ale chyba powinno to wystarczyc jaki koncept
}

chociaz sam nie lubie slowa manager, mozesz uzyc slowa service czy co tam Ci sie podoba

DI robisz PerRequestScope i unitofwork jest wstrzykiwany w konstruktorze

0
fasadin napisał(a):

Controller zawiera Manager'a

Manager zawiera UnitOfWork

w controlerze wywolujesz

manager.FeatureA()

w managerze

public async Task FeatureA() 
{
  var repositoryA = this.unitOfWork.RepositoryA;
  
  var result = repositoryA,GimmeAll();

  await result.DoSomethingElse(); //  nie chcialo mi sie pisac logiki biznesowej, ale chyba powinno to wystarczyc jaki koncept
}

chociaz sam nie lubie slowa manager, mozesz uzyc slowa service czy co tam Ci sie podoba

DI robisz PerRequestScope i unitofwork jest wstrzykiwany w konstruktorze

Super! Świetne wytłumaczenie. Mam jeszcze jedno pytanie odnośnie dobrych praktyk w tym miejscu. Czy jeżeli akcja otrzymuje zbindowany ViewModel, np. z danymi użytkownika do rejestracji i chcę tymi danymi zasilić manager (serwis), czy lepszym podejściem jest tutaj przekazanie całego obiektu ViewModel do serwisu czy może robicie go na pojedyncze zmienne:


        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            _userService.RegisterAsync(model.email, model.FistName, model.LastName, model.Password ...)
        }

W sumie to chodzi mi o to czy serwis powinien przyjmować ViewModele otrzymane z bindowania formularza czy powinien być bardziej uniwersalny.

0

przekazuj caly viewModel zgodnie z zasada

YAGNI

Jezeli zauwazysz, ze ten sam feature bedzie uzywany dla roznych kontrolerow ale w sumie przyjmuja inne dane wtedy mozesz zastosowac wspolny obiekt, ale poki nie masz tego. Nie przejmuj sie tym

1
_context = context;
Users = new UserRepository(_context);
Requests = new RequestRepository(_context);
TimeSlots = new TimeSlotRepository(_context);
A = new ARepository(_context);
B = new BRepository(_context);
C = new CRepository(_context);
D = new DRepository(_context);
E = new ERepository(_context);
F = new FRepository(_context);
(...)

Jakiż jest sens?

0

Ale po co UoW ma być zależny od każdego repozytorium? Jak to się ma do Open/Closed Principle?

0

Masz racje jest to zgodnie OCP, ale po co to w ogóle robić.?

Dla mnie to nie jest UOW. Ja to identyfikuje jako fabrykę Repository'iów z wrapem na sessje ORM, która pełni funkcje UOW oraz Data Mapper'a. Co jest dla mnie niezrozumiałe, ponieważ do tworzenia obiektów, które będą wstrzykiwane do serwisów jest Kontener.

UOW jest od śledzenia zmian w obiektach Domenowych/Persistence zależy jak to sobie zaimplementujesz. Ty jak pamiętam jesteś zwolennikiem oddzielania modelu Persistence'i. W teorii Repository nie musi nic wiedzieć o UOW.

Nie wydaje ci się dziwne, że w UOW znajdują się Rrepositories?

1
._. napisał(a):

Masz racje jest to zgodnie OCP

Niby w jaki sposób może to być zgodne z OCP, skoro dodanie repozytorium wymaga zmiany działającego UoW?

Nie wydaje ci się dziwne, że w UOW znajdują się Rrepositories?

Bardzo, dlatego właśnie o to spytałem.

0
somekind napisał(a):
._. napisał(a):

Masz racje jest to zgodnie OCP

Niby w jaki sposób może to być zgodne z OCP, skoro dodanie repozytorium wymaga zmiany działającego UoW?

Nie wydaje ci się dziwne, że w UOW znajdują się Rrepositories?

Bardzo, dlatego właśnie o to spytałem.

Dobrze w takim razie jakie byłoby lepsze podejście do tej sprawy? Wywalenie repozytorium z UoW? tylko wtedy przekazywać do serwisu oddzielnie odpowiednie repozytorium/repozytoria i UoW?

0

A co takiego robią serwisy, które używają repositoriow, a raczej, jakie mają metody? Jest tam coś oprucz Update, Create, Delete, Get?

0
._. napisał(a):

A co takiego robią serwisy, które używają repositoriow, a raczej, jakie mają metody? Jest tam coś oprucz Update, Create, Delete, Get?

Trochę nie rozumiem celu twojego pytania.

Przykładowo serwis ma metodę RegisterAsync, AuthenticateAsync.

Natomiast repozytoria mają prawdopodobnie będą miały wyłącznie różne odmiany metod Get i Add. Jako że usuwam obiekty z bazy poprzez dodatkową kolumnę bool IsDeleted, zrezygnowałem z Delete.

0

Jakie dokładnie odmiany Get? Czy używasz repositoriów w serwisach, które zwracają obiekty dla View?

0

GetAll, GetById, GetByEmail i tym podobne, w zależności od obiektu różnie będą wyglądać. Używam repozytoriów w serwisach, których metody zwracają ViewModele, z tym że nie wiem czy to dobre podejście i nie powinienem wydzielić tego przypisania ViewModeli od serwisów i umieścić tego na kontrolerach. Z tym że wtedy mamy całą masę kodu na akcji kontrolera, gdy ViewModel jest złożony.

1

Repozytoria nie służą do pobierania obiektów dla View. Repozytoria pobierają stan obiektu domenowego serwis wykonuje na nim metodę, która odpowiada logice, po czym repozytorium utrwala jego stan. Powinieneś mieć oddzielny serwis do pobierania obiektów do View. ViewModel powinien wynikać z logiki View a nie serwisu aplikacji. ViewModel jest po to, żeby nałożyć jakąś dodatkową "Fasadę" na obiekt, który zwraca serwis, nie musisz nazywać tego, co zwraca serwis aplikacji ViewModelem od tak dla zasady.

0
._. napisał(a):

Repozytoria nie służą do pobierania obiektów dla View. Repozytoria pobierają stan obiektu domenowego serwis wykonuje na nim metodę, która odpowiada logice, po czym repozytorium utrwala jego stan. Powinieneś mieć oddzielny serwis do pobierania obiektów do View. ViewModel powinien wynikać z logiki View a nie serwisu aplikacji. ViewModel jest po to, żeby nałożyć jakąś dodatkową "Fasadę" na obiekt, który zwraca serwis, nie musisz nazywać tego, co zwraca serwis aplikacji ViewModelem od tak dla zasady.

Chyba się nie zrozumieliśmy. Tak właśnie stosuję repozytoria. Do tej pory mam instancję UoW, która posiada repozytoria, dzięki nim pobieram obiekty domenowe z bazy lub lub dodaje je tam i jedyne wykorzystanie repozytoriów znajduje się w serwisach. Nie stosuję w repozytoriach żadnych wywołań które zwracają mi ViewModele. Jednocześnie dbContext.Save() odbywa się przez wywołanie metody zapisującej z UoW która jest wywołana w serwisie. W serwisach mapuję obiekty domenowe na DTO i przekazuję wyżej do kontrolera lub też tworzę ViewModel, który przekazuję później do View. Czy sugerujesz że serwisu nie powinny zwracać ViewModel a jedynie DTO, za to powinien być stworziny nowy serwis który mapowałby te DTO na ViewModel?

Wciąż zostaje bez odpowiedzi na wcześniej zadane pytanie:

Czy wywalenie repozytorium z UoW jest dobrym pomysłem i czy wtedy przekazywać do serwisu oddzielnie odpowiednie repozytorium/repozytoria i UoW?

1

Ale ty nie kumasz co jest co. Obiekt domenowy to nie jest ot tak obiekt, który zapisujesz do bazy ty mówisz o jakiejś strukturze dla mapowania obiektowo relacyjnego, a nie obiekcie domenowym. Obiekt domenowy w teorii nie musi mieć żadnych pól, które odpowiadają krotce relacji w bazie jak np."Title, Category, Content etc."

W serwisach mapuję obiekty domenowe na DTO i przekazuję wyżej do kontrolera lub też tworzę ViewModel, który przekazuję później do View.

No i to jest bez sensu.

Wciąż zostaje bez odpowiedzi na wcześniej zadane pytanie:

Powinieneś je wstrzykiwać, do serwisów, które ich potrzebują.

1
Wielki Karp napisał(a):

Dobrze w takim razie jakie byłoby lepsze podejście do tej sprawy? Wywalenie repozytorium z UoW?

Tak.

tylko wtedy przekazywać do serwisu oddzielnie odpowiednie repozytorium/repozytoria i UoW?

Oczywiście.

Wielki Karp napisał(a):

W serwisach mapuję obiekty domenowe na DTO i przekazuję wyżej do kontrolera lub też tworzę ViewModel, który przekazuję później do View.

No i po co Ci w takim razie te repozytoria? Jako wrapper na ORM?

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