Korzystam z EF Core 1.0 (wcześniej znany jako EF7) i ASP.NET Core 1.0 (wcześniej znany jako ASP.NET 5) dla RESTful API .Jednostka Entity Framework Core 1.0 z oprogramowaniem warstwy pośredniej Asp.Net Core lub filtrem Mvc
Chciałbym, aby jakaś jednostka pracy była ustawiona na żądanie http w taki sposób, aby podczas odpowiadania na żądanie HTTP WSZYSTKIE zmiany wprowadzone do DbContext były zapisywane w bazie danych, lub żaden nie zostanie zapisany (jeśli był jakiś wyjątek, na przykład).
W przeszłości użyłem WebAPI2 do tego celu z NHibernate za pomocą filtra akcji, w którym rozpocząłem transakcję po wykonaniu akcji, a po wykonaniu operacji kończę transakcję i zamykam sesję. To był sposób zalecany pod adresem http://isbn.directory/book/9781484201107
Jednak teraz używam Asp.Net Core (z Asp.Net Core Mvc, chociaż nie powinno to być istotne) i Entity Framework, który, jak zrozumiałem, już implementuje jednostkę pracy.
Myślę, że posiadanie oprogramowania pośredniego podłączonego do potoku ASP.NET (przed MVC) byłoby właściwym sposobem robienia rzeczy. Więc wniosek pójdzie:
PIPELINE ASP.NET: MyUnitOfWorkMiddleware ==> MVC Controller ==> Repository ==> MVC Controller ==> MyUnitOfWorkMiddleware
Myślałam o konieczności to middleware zapisać DbContext zmienia się, jeśli nie wystąpił wyjątek, więc w implementacjach mojego repozytorium nie muszę nawet wykonywać dbcontext.SaveChanges()
i wszystko byłoby jak scentralizowana transakcja. W pseudokod Myślę, że to byłoby coś takiego:
class MyUnitOfWorkMiddleware
{
//..
1-get an instance of DbContext for this request.
try {
2-await the next item in the pipeline.
3-dbContext.SaveChanges();
}
catch (Exception e) {
2.1-rollback changes (simply by ignoring context)
2.2-return an http error response
}
}
Czy to ma sens? Czy ktokolwiek ma przykład czegoś podobnego? Nie mogę znaleźć żadnej dobrej praktyki lub rekomendacji w tym zakresie.
Ponadto, jeśli zastosuję to podejście na moim poziomie kontrolera MVC, nie będę miał dostępu do żadnego identyfikatora zasobu utworzonego przez bazę danych podczas POSTING nowego zasobu, ponieważ identyfikator nie zostanie wygenerowany, dopóki zmiany dbContext nie zostaną zapisane (później włączone w potoku w moim oprogramowaniu pośrednim po zakończeniu wykonywania przez kontroler). Co się stanie, jeśli będę potrzebował uzyskać dostęp do nowo utworzonego identyfikatora zasobu w moim kontrolerze?
Każda rada byłaby bardzo ceniona!
UPDATE 1: znalazłem problem z moim podejściem do wykorzystania middleware do osiągnięcia tego celu, ponieważ instancja DbContext w middleware nie jest taka sama jak podczas MVC (i repozytoriów) życia. Zobacz pytanie Entity Framework Core 1.0 DbContext not scoped to http request
UPDATE 2:ja jeszcze nie znalazłem dobre rozwiązanie. Zasadniczo są to moje dotychczasowe opcje:
- Zapisz zmiany w DB tak szybko, jak to możliwe. Oznacza to zapisanie go w samej implementacji repozytorium. Problem z tym podejściem polega na tym, że dla żądania HTTP mogę chcieć użyć kilku repozytoriów (np.e: zapisz coś w bazie danych, a następnie prześlij blob do pamięci w chmurze) i aby mieć Jednostkę Pracy, będę musiał zaimplementować repozytorium, które zajmuje się więcej niż jedną jednostką lub nawet więcej niż jedną metodą persistance (DB i Blob Storage), który pokonuje cały cel:
Implementacja Action Filter, w której zawijam całe wykonanie akcji w transakcji DB. Na końcu wykonywania akcji kontrolera, jeśli nie ma wyjątków, to zgłaszam chance do DB, ale jeśli są wyjątki, wycofuję i odrzucam kontekst. Problem polega na tym, że działanie mojego kontrolera może wymagać wygenerowanego identyfikatora Entity w celu zwrócenia go do klienta http (tzn. Jeśli dostanę POST/api/samochody, chciałbym zwrócić 201 Zaakceptowany z nagłówkiem lokalizacji, który identyfikuje nowy zasób stworzony w/api/cars/123, a Id 123 nie byłby jeszcze dostępny, ponieważ encja nie została zapisana w DB, a Id jest nadal tymczasowy 0). Przykład w działaniu sterownika dla żądania POST czasownika:
return CreatedAtRoute("GetCarById", new { carId= carSummaryCreated.Id }, carSummaryCreated); //carSummaryCreated.Id would be 0 until the changes are saved in DB
Jak mogę mieć działanie całego regulatora owinięty w transakcji DB i jednocześnie mieć dostępny dowolny identyfikator generowany przez bazę danych w celu zwrócić go w odpowiedzi HTTP z kontrolera? Lub .. jest jakiś elegancki sposób na zastąpienie odpowiedzi http i ustawić Id na poziomie filtra akcji po zatwierdzeniu zmian DB?
UPDATE 3: Zgodnie nathanaldensr „s komentarzu mogę uzyskać najlepsze z obu światów (owijanie wykonanie działania mojego kontrolera w transakcji DB _ UOW, a także znając ID nowego zasobu stworzonego jeszcze przed DB zatwierdza zmiany), wykorzystując wygenerowany kod Guids zamiast polegać na bazie danych, aby wygenerować Guid.
Jest to dobre rozwiązanie dzięki – iberodev
Podoba mi się, że można udekorować dowolną akcję lub kontroler za pomocą filtra jednostki pracy, aby zawinąć wszystko w transakcji podczas cyklu życia mvc. – iberodev
Problem polega na tym, co należy zrobić, gdy potrzebny jest identyfikator jednostki, aby dalej wykonywać czynności w ramach tego samego żądania http. Identyfikator nie zostanie wygenerowany do czasu wprowadzenia zmian w DB. Przy takim podejściu zmiany nie są zapisywane, dopóki sterownik nie sfinalizuje wykonania. – iberodev