Generic Repository Z EF 4.1 o co chodzi


145

Zagłębiając się w DbContext, DbSet i powiązane interfejsy, zastanawiam się, dlaczego miałbyś zaimplementować oddzielne repozytorium „Generic” wokół tych implementacji?

Wygląda na to, że DbContext i IDbSet robią wszystko, czego potrzebujesz, i zawierają „Unit Of Work” wewnątrz DbContext.

Czy czegoś mi tu brakuje, czy też wydaje się, że ludzie lubią dodawać kolejną warstwę zależności bez powodu.


Jest to kwestia nieco sporna / oparta na opiniach. Omówiłem to tutaj .
Amit Joshi,

Odpowiedzi:


202

Masz rację. DbContextjest implementacją wzorca jednostki pracy i IDbSetjest implementacją wzorca repozytorium.

Repozytoria są obecnie bardzo popularne i nadużywane. Wszyscy ich używają tylko dlatego, że istnieją dziesiątki artykułów o tworzeniu repozytorium dla frameworka jednostek, ale nikt tak naprawdę nie opisuje wyzwań związanych z tą decyzją.

Główne powody korzystania z repozytorium to zazwyczaj:

  • Ukryj EF z górnej warstwy
  • Spraw, by kod był lepiej testowalny

Pierwszym powodem jest pewna czystość architektoniczna i świetny pomysł, że jeśli uniezależnisz swoje wyższe warstwy od EF, możesz później przełączyć się na inną strukturę trwałości. Ile razy widziałeś coś takiego w prawdziwym świecie? Z tego powodu praca z EF jest znacznie trudniejsza, ponieważ repozytorium musi uwidaczniać wiele dodatkowych funkcji opakowujących to, na co domyślnie zezwala EF.

W tym samym czasie zawijanie kodu EF może zapewnić lepszą organizację kodu i przestrzegać reguły oddzielenia obaw. Dla mnie może to być jedyna prawdziwa zaleta repozytorium i jednostki pracy, ale musisz zrozumieć, że przestrzeganie tej reguły z EF może sprawić, że Twój kod będzie łatwiejszy w utrzymaniu i czytelniejszy, ale na początku wysiłek związany z utworzeniem aplikacji będzie znacznie wyższy i w przypadku mniejszych aplikacji może to być niepotrzebna złożoność.

Drugi powód jest częściowo poprawny. Dużą wadą EF jest sztywna architektura, z której trudno jest wyszydzać, więc jeśli chcesz przetestować jednostkową górną warstwę, musisz w jakiś sposób opakować EF, aby umożliwić mockowanie jego implementacji. Ale ma to wiele innych konsekwencji, które tutaj opisałem .

Śledzę bloga Ayende użytkownika . Jeśli kiedykolwiek używałeś NHibernate, prawdopodobnie znasz jego artykuły. Ten facet niedawno napisał kilka artykułów przeciwko używaniu repozytorium z NHibernate, ale NHibernate jest znacznie lepiej do podrobienia.


3
Możesz mockować IDbSet, możesz także zdefiniować niestandardowy interfejs w swoim pochodnym kontekście, ale to wszystko. Gdy Twój kod użyje ChangeTracker, Entries lub czegokolwiek innego, opakowanie ich wszystkich będzie wymagało dużego wysiłku.
Ladislav Mrnka

1
Tak, EF nie jest narzędziem zorientowanym na wydajność. Przynajmniej MS ma wiele możliwości, aby ulepszyć to w przyszłych wersjach.
Ladislav Mrnka

2
@chiccodoro: Racja. Ale kiedy twoja fałszywa klasa ujawnia IQueryablelub akceptuje Expression<>jako parametr, który jest wewnętrznie wysyłany do zapytania Linq-to-entity, definiujesz logikę poza pozorowanym komponentem z efektami ubocznymi, których nie można przetestować za pomocą testów jednostkowych.
Ladislav Mrnka

8
Jeśli używam DbSet i BdContext bezpośrednio w mojej warstwie biznesowej, muszę odwoływać się do EntityFramework.dll tam, a także w moim projekcie DataLayer. Już samo to mówi mi, że potrzebuje jakiegoś opakowania.
Ingó Vals

2
downvote: incomplete - abstrakcja EF za interfejsem repozytorium może spowodować, że dokładnie ten sam kod klienta będzie działał zarówno w SL, jak i WPF.
h.alex,

21

Zmagam się z tymi samymi problemami, a makieta do testowania jednostkowego warstw EF jest ważna. Ale natknąłem się na ten świetny artykuł, który wyjaśnia, jak skonfigurować EF 4.1 DbContext, aby był makowalny, upewniając się, że pochodny DbContext zaimplementował ogólny interfejs i uwidacznia IDbSet zamiast DbSet. Ponieważ korzystam z podejścia Database First, ponieważ nasza baza danych już istnieje, po prostu zmodyfikowałem szablony T4 używane do generowania mojego pochodnego DbContext w celu wygenerowania go w celu zwrócenia interfejsów IDbSet, a także wyprowadzenia z mojego interfejsu ogólnego. W ten sposób całość można łatwo wyszydzić i nie trzeba implementować własnej jednostki pracy ani wzorca repozytorium. Po prostu napisz kod usługi, aby wykorzystać ogólny interfejs, a kiedy przejdziesz do testu jednostkowego,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/


5

Jednym z powodów tworzenia repozytorium jest to, że możesz ukryć implementację DBSet i DbContext, jeśli zdecydujesz się przejść z EntityFramework do czegoś innego lub odwrotnie.

Na przykład używałem NHibernate i zawinąłem wszystkie wywołania tego frameworka wewnątrz moich klas repozytorium. Zwracają IEnumerable, aby były „generyczne”, a moje repozytoria mają standardowe operacje CRUD (aktualizacja, usuwanie itp.). Już dawno przeniosłem się do Entity Framework. Robiąc to, nie musiałem niczego zmieniać w moich klasach ViewModel ani poza nimi, ponieważ wskazywały one na moje repozytorium - potrzebowałem tylko zmienić wnętrze mojego repozytorium. To znacznie ułatwiło życie podczas migracji.

(Użyłem NHibernate, ponieważ łączymy się z ISeries, a w tamtym czasie nie było opłacalnych implementacji korzystających z EF z ISeries. Jedyną dostępną metodą było zapłacenie IBM 12 000 USD za DB2Connect)


„Prawie” (jeśli chodzi o ukrywanie DBSet i DbContext) okaże się, że nie musisz ujawniać EF żadnym konsumentom (na przykład jeśli korzystasz z DI), ale potrzebujesz interfejsu, który ujawnia właściwości IDbSet <T> lub idź o krok dalej i zamiast tego wpisz wszystkie swoje właściwości jako IQueryable <T>, ale chodzi mi o to, że możesz całkowicie ukryć swoją zależność od DbSet i DbContext. Operacje CRUD można następnie zapisać jako metody rozszerzające, można napisać wiele metod rozszerzających dla różnych magazynów zapasowych. Nie będziesz jednak ukrywać użycia LINQ.
Shaun Wilson,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.