Przeszedłem wiele ścieżek i stworzyłem wiele implementacji repozytoriów w różnych projektach i ... Rzuciłem ręcznik i poddałem się, oto dlaczego.
Kodowanie wyjątku
Czy kodujesz pod kątem 1% szansy, że Twoja baza danych zmieni się z jednej technologii na inną? Jeśli myślisz o przyszłym stanie swojej firmy i mówisz, że tak, to jest taka możliwość, a) muszą mieć dużo pieniędzy, aby pozwolić sobie na migrację do innej technologii DB lub b) wybierasz technologię DB dla przyjemności lub c ) coś poszło nie tak z pierwszą technologią, z której zdecydowałeś się skorzystać.
Po co wyrzucać bogatą składnię LINQ?
LINQ i EF zostały opracowane, aby można było z nimi robić fajne rzeczy, aby odczytywać i przechodzić przez wykresy obiektów. Tworzenie i utrzymywanie repozytorium, które daje taką samą elastyczność, jest potwornym zadaniem. Z mojego doświadczenia wynika, że za każdym razem, gdy tworzyłem repozytorium, ZAWSZE miałem wyciek logiki biznesowej do warstwy repozytorium, aby zwiększyć wydajność zapytań i / lub zmniejszyć liczbę trafień do bazy danych.
Nie chcę tworzyć metody dla każdej permutacji zapytania, które muszę napisać. Równie dobrze mógłbym napisać procedury składowane. Nie chcę GetOrder
, GetOrderWithOrderItem
, GetOrderWithOrderItemWithOrderActivity
, GetOrderByUserId
, i tak dalej ... Po prostu chcę dostać główną jednostkę i trawers i zawierają wykres obiektu jak ja więc proszę.
Większość przykładów repozytoriów to bzdury
Chyba że tworzysz coś NAPRAWDĘ podstawowego, takiego jak blog lub coś, co Twoje zapytania nigdy nie będą tak proste, jak 90% przykładów, które znajdziesz w Internecie, otaczających wzór repozytorium. Nie mogę tego wystarczająco podkreślić! To jest coś, co trzeba przeczołgać się przez błoto, żeby się dowiedzieć. Zawsze będzie jedno zapytanie, które zepsuje twoje doskonale przemyślane repozytorium / rozwiązanie, które stworzyłeś, i dopiero w tym momencie, gdy ponownie zgadujesz siebie i zaczyna się dług techniczny / erozja.
Nie testuj mnie jednostkowo, bracie
Ale co z testami jednostkowymi, jeśli nie mam repozytorium? Jak będę kpić? Proste, nie masz. Spójrzmy na to z obu stron:
Brak repozytorium - możesz mockować przy DbContext
użyciu IDbContext
lub innych sztuczek, ale wtedy naprawdę testujesz jednostkowo LINQ to Objects, a nie LINQ to Entities, ponieważ zapytanie jest określane w czasie wykonywania ... OK, więc to nie jest dobre! Więc teraz to zależy od testu integracji, aby to objąć.
Z repozytorium - możesz teraz mockować swoje repozytoria i testować jednostki między warstwami. Świetnie, prawda? Cóż, nie do końca ... W powyższych przypadkach, w których musisz przeciekać logikę do warstwy repozytorium, aby zapytania były bardziej wydajne i / lub mniej trafień do bazy danych, jak testy jednostkowe mogą to pokryć? Jest teraz w warstwie repozytorium i nie chcesz testować, IQueryable<T>
prawda? Bądźmy również szczerzy, twoje testy jednostkowe nie będą obejmować zapytań, które mają .Where()
klauzulę 20 linii i.Include()
To zbiór relacji i ponownie trafia do bazy danych, aby wykonać wszystkie inne czynności, bla, bla, bla tak czy inaczej, ponieważ zapytanie jest generowane w czasie wykonywania. Ponieważ utworzyłeś repozytorium, aby utrzymać ignorancję w zakresie trwałości wyższych warstw, jeśli chcesz teraz zmienić technologię bazy danych, przepraszam, że testy jednostkowe na pewno nie zagwarantują tych samych wyników w czasie wykonywania, wracając do testów integracyjnych. Więc cały punkt repozytorium wydaje się dziwny ...
2 centy
Utraciliśmy już wiele funkcji i składni, gdy używamy EF zamiast zwykłych procedur składowanych (wstawianie zbiorcze, usuwanie zbiorcze, CTE itp.), Ale koduję również w C #, więc nie muszę wpisywać binarnego. Używamy EF, dzięki czemu możemy mieć możliwość korzystania z różnych dostawców i pracy z grafami obiektów w przyjemny, powiązany sposób między wieloma rzeczami. Niektóre abstrakcje są przydatne, a niektóre nie.
Coding for the exception
: Używanie repozytoriów nie oznacza możliwości zmiany silnika bazy danych. Chodzi o oddzielenie biznesu od wytrwałości.DbSet
jest repozytorium iDbContext
jest jednostką pracy . Po co implementować wzorzec repozytorium, skoro ORM już to robi za nas! Do testów wystarczy zmienić dostawcę naInMemory
. I zrób swoje testy! Jest to dobrze udokumentowane w MSDN.Wzorzec repozytorium jest abstrakcją . Ma to na celu zmniejszenie złożoności i uczynienie reszty kodu trwałą ignorancją. Jako bonus umożliwia pisanie testów jednostkowych zamiast testów integracyjnych .
Problem polega na tym, że wielu programistów nie rozumie celu wzorców i nie tworzy repozytoriów, które przeciekają określone informacje do dzwoniącego (zwykle przez ujawnienie
IQueryable<T>
). W ten sposób nie odnoszą korzyści z bezpośredniego korzystania z sali operacyjnej / M.Zaktualizuj, aby zająć się inną odpowiedzią
Kodowanie wyjątku
Korzystanie z repozytoriów nie polega na możliwości przełączania technologii trwałości (tj. Zmiany bazy danych lub korzystania z usługi sieciowej itp.). Chodzi o oddzielenie logiki biznesowej od trwałości, aby zmniejszyć złożoność i powiązania.
Testy jednostkowe a testy integracyjne
Nie piszesz testów jednostkowych dla repozytoriów. Kropka.
Ale wprowadzając repozytoria (lub jakąkolwiek inną warstwę abstrakcji między trwałością a biznesem), możesz pisać testy jednostkowe dla logiki biznesowej. tzn. nie musisz się martwić, że testy zakończą się niepowodzeniem z powodu nieprawidłowo skonfigurowanej bazy danych.
Co do pytań. Jeśli używasz LINQ, musisz również upewnić się, że zapytania działają, tak samo jak w przypadku repozytoriów. i odbywa się to za pomocą testów integracyjnych.
Różnica polega na tym, że jeśli nie pomieszałeś swojej firmy z instrukcjami LINQ, możesz mieć 100% pewności, że to Twój kod trwałości zawodzi, a nie coś innego.
Jeśli przeanalizujesz swoje testy, zobaczysz również, że są one znacznie czystsze, jeśli nie masz mieszanych obaw (np. Logika LINQ + Business)
Przykłady repozytoriów
Większość przykładów to bzdury. to jest bardzo prawdziwe. Jeśli jednak wygooglujesz jakiś wzór projektu, znajdziesz wiele kiepskich przykładów. Nie ma powodu, aby unikać używania wzoru.
Zbudowanie poprawnej implementacji repozytorium jest bardzo łatwe. W rzeczywistości musisz przestrzegać tylko jednej zasady:
Nie dodawaj niczego do klasy repozytorium aż do momentu, gdy tego potrzebujesz
Wielu programistów jest leniwych i próbuje utworzyć ogólne repozytorium i użyć klasy bazowej z wieloma metodami, których mogą potrzebować. YAGNI. Piszesz klasę repozytorium raz i przechowujesz ją tak długo, jak działa aplikacja (może to być lata). Po co to spieprzyć będąc leniwym. Utrzymuj go w czystości bez dziedziczenia klasy bazowej. Ułatwi to czytanie i konserwację.
(Powyższe stwierdzenie jest wskazówką, a nie prawem. Klasę bazową można bardzo dobrze zmotywować. Pomyśl tylko, zanim ją dodasz, aby dodać ją z właściwych powodów)
Starocie
Wniosek:
Jeśli nie masz nic przeciwko umieszczaniu instrukcji LINQ w kodzie biznesowym ani nie przejmujesz się testami jednostkowymi, nie widzę powodu, aby nie używać Entity Framework bezpośrednio.
Aktualizacja
Napisałem na blogu zarówno o wzorcu repozytorium, jak io tym, co naprawdę oznacza „abstrakcja”: http://blog.gauffin.org/2013/01/repository-pattern-done-right/
Zaktualizuj 2
Ponownie: jednostka z ponad 20 polami jest nieprawidłowo modelowana. To istota BOGA. Rozbicie go.
Nie twierdzę, że
IQueryable
to nie było do zadawania pytań. Mówię, że nie jest to właściwe dla warstwy abstrakcji, takiej jak wzór repozytorium, ponieważ jest nieszczelna. Nie ma dostawcy LINQ To Sql w 100% kompletnego (takiego jak EF).Wszystkie mają specyficzne dla implementacji rzeczy, takie jak używanie przyspieszonego / leniwego ładowania lub wykonywanie instrukcji SQL „IN”. Wystawienie
IQueryable
w repozytorium zmusza użytkownika do poznania tych wszystkich rzeczy. Zatem cała próba wyodrębnienia źródła danych kończy się całkowitą porażką. Po prostu dodajesz złożoność bez żadnych korzyści z bezpośredniego korzystania z OR / M.Albo poprawnie zaimplementuj wzorzec repozytorium, albo po prostu go nie używaj.
(Jeśli naprawdę chcesz obsługiwać duże jednostki, możesz połączyć wzorzec Repozytorium ze wzorcem Specification . To daje pełną abstrakcję, którą można również przetestować).
źródło
IMO zarówno
Repository
abstrakcja, jak iUnitOfWork
abstrakcja zajmują bardzo cenne miejsce w każdym znaczącym rozwoju. Ludzie będą spierać się o szczegóły implementacji, ale tak jak jest wiele sposobów na oskórowanie kota, tak istnieje wiele sposobów na wdrożenie abstrakcji.Twoje pytanie dotyczy konkretnie używania lub nie używania i dlaczego.
Jak bez wątpienia zdałeś sobie sprawę, że masz już oba te wzorce wbudowane w Entity Framework,
DbContext
jestUnitOfWork
iDbSet
jestRepository
. Zasadniczo nie ma potrzeby testowania jednostekUnitOfWork
lubRepository
samych siebie, ponieważ po prostu ułatwiają one między klasami a podstawowymi implementacjami dostępu do danych. To, co będziesz musiał ciągle robić, to kpić z tych dwóch abstrakcji podczas testowania jednostkowego logiki usług.Możesz kpić, fałszować lub cokolwiek innego, używając zewnętrznych bibliotek dodając warstwy zależności kodu (których nie kontrolujesz) między logiką wykonującą testowanie a logiką testowaną.
Więc punkt moll jest to, że posiadanie własnego abstrakcji dla
UnitOfWork
iRepository
daje maksymalną kontrolę i elastyczność podczas szydząc swoje testy jednostkowe.Wszystko bardzo dobrze, ale dla mnie prawdziwą mocą tych abstrakcji jest to, że zapewniają one prosty sposób zastosowania technik programowania zorientowanego na aspekt i przestrzegania zasad SOLID .
Masz więc swoje
IRepository
:public interface IRepository<T> where T : class { T Add(T entity); void Delete(T entity); IQueryable<T> AsQueryable(); }
I jego realizacja:
public class Repository<T> : IRepository<T> where T : class { private readonly IDbSet<T> _dbSet; public Repository(PPContext context) { _dbSet = context.Set<T>(); } public T Add(T entity) { return _dbSet.Add(entity); } public void Delete(T entity) { _dbSet.Remove(entity); } public IQueryable<T> AsQueryable() { return _dbSet.AsQueryable(); } }
Jak dotąd nic niezwykłego, ale teraz chcemy dodać trochę logowania - łatwe dzięki dekoratorowi logowania .
public class RepositoryLoggerDecorator<T> : IRepository<T> where T : class { Logger logger = LogManager.GetCurrentClassLogger(); private readonly IRepository<T> _decorated; public RepositoryLoggerDecorator(IRepository<T> decorated) { _decorated = decorated; } public T Add(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() ); T added = _decorated.Add(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); return added; } public void Delete(T entity) { logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); _decorated.Delete(entity); logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString()); } public IQueryable<T> AsQueryable() { return _decorated.AsQueryable(); } }
Wszystko gotowe i bez zmian w naszym istniejącym kodzie . Istnieje wiele innych przekrojowych problemów, które możemy dodać, takich jak obsługa wyjątków, buforowanie danych, walidacja danych lub cokolwiek innego, a podczas naszego procesu projektowania i budowania jest to najcenniejsza rzecz, jaką mamy, która pozwala nam dodawać proste funkcje bez zmiany istniejącego kodu to nasza
IRepository
abstrakcja .Teraz wiele razy widziałem to pytanie w StackOverflow - „Jak sprawić, by Entity Framework działało w środowisku z wieloma dzierżawcami?”.
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Jeśli masz
Repository
abstrakcję, odpowiedź brzmi: „łatwo dodać dekorator”public class RepositoryTennantFilterDecorator<T> : IRepository<T> where T : class { //public for Unit Test example public readonly IRepository<T> _decorated; public RepositoryTennantFilterDecorator(IRepository<T> decorated) { _decorated = decorated; } public T Add(T entity) { return _decorated.Add(entity); } public void Delete(T entity) { _decorated.Delete(entity); } public IQueryable<T> AsQueryable() { return _decorated.AsQueryable().Where(o => true); } }
IMO, zawsze powinieneś umieścić prostą abstrakcję na każdym komponencie strony trzeciej, do którego będą się odnosić w więcej niż kilku miejscach. Z tej perspektywy ORM jest idealnym kandydatem, ponieważ odwołuje się do niego w znacznej części naszego kodu.
Odpowiedź, która zwykle przychodzi na myśl, gdy ktoś mówi „dlaczego powinienem mieć abstrakcję (np.
Repository
) Na temat tej lub innej biblioteki innej firmy”, brzmi „dlaczego nie?”Dekoratory PS są niezwykle łatwe do zastosowania przy użyciu kontenerów IoC, takich jak SimpleInjector .
[TestFixture] public class IRepositoryTesting { [Test] public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository() { Container container = new Container(); container.RegisterLifetimeScope<PPContext>(); container.RegisterOpenGeneric( typeof(IRepository<>), typeof(Repository<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryLoggerDecorator<>)); container.RegisterDecorator( typeof(IRepository<>), typeof(RepositoryTennantFilterDecorator<>)); container.Verify(); using (container.BeginLifetimeScope()) { var result = container.GetInstance<IRepository<Image>>(); Assert.That( result, Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>))); Assert.That( (result as RepositoryTennantFilterDecorator<Image>)._decorated, Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>))); } } }
źródło
Po pierwsze, jak sugeruje pewna odpowiedź, sam EF jest wzorcem repozytorium, nie ma potrzeby tworzenia dalszej abstrakcji tylko po to, aby nazwać go repozytorium.
Mockable Repository for Unit Tests, czy naprawdę go potrzebujemy?
Pozwalamy EF komunikować się, aby przetestować bazę danych w testach jednostkowych, aby przetestować naszą logikę biznesową bezpośrednio względem testowej bazy danych SQL. Nie widzę żadnej korzyści z posiadania makiety żadnego wzorca repozytorium. Co naprawdę jest złego w wykonywaniu testów jednostkowych w testowej bazie danych? Ponieważ operacje masowe nie są możliwe i kończy się na pisaniu surowego kodu SQL. SQLite in memory jest idealnym kandydatem do wykonywania testów jednostkowych na rzeczywistej bazie danych.
Niepotrzebna abstrakcja
Czy chcesz utworzyć repozytorium tylko po to, aby w przyszłości łatwo zastąpić EF na NHbibernate itp. Lub cokolwiek innego? Brzmi świetnie, ale czy jest naprawdę opłacalny?
Linq zabija testy jednostkowe?
Chciałbym zobaczyć przykłady tego, jak może zabijać.
Dependency Injection, IoC
Wow, to świetne słowa, na pewno świetnie wyglądają w teorii, ale czasami trzeba wybrać kompromis między świetnym projektem a świetnym rozwiązaniem. Wykorzystaliśmy to wszystko i skończyło się na tym, że wyrzuciliśmy wszystko do kosza i wybraliśmy inne podejście. Rozmiar a szybkość (rozmiar kodu i szybkość rozwoju) ma ogromne znaczenie w prawdziwym życiu. Użytkownicy potrzebują elastyczności, nie obchodzi ich, czy Twój kod jest świetny w projektowaniu pod względem DI czy IoC.
Chyba że tworzysz Visual Studio
Wszystkie te wspaniałe projekty są potrzebne, jeśli tworzysz złożony program, taki jak Visual Studio lub Eclipse, który będzie rozwijany przez wiele osób i musi być wysoce konfigurowalny. Cały wspaniały wzorzec rozwoju pojawił się po latach rozwoju tych IDE i ewoluowały w miejscu, w którym wszystkie te wspaniałe wzorce projektowe mają tak duże znaczenie. Ale jeśli robisz prostą listę płac opartą na sieci lub prostą aplikację biznesową, lepiej jest rozwijać swój program z czasem, zamiast spędzać czas na tworzeniu go dla milionów użytkowników, gdzie będzie wdrażany tylko dla setek użytkowników.
Repozytorium jako widok filtrowany - ISecureRepository
Z drugiej strony repozytorium powinno być filtrowanym widokiem EF, który chroni dostęp do danych, stosując niezbędny wypełniacz w oparciu o aktualnego użytkownika / rolę.
Ale to komplikuje repozytorium jeszcze bardziej, ponieważ kończy się ono w ogromnej bazie kodu do utrzymania. W końcu ludzie tworzą różne repozytoria dla różnych typów użytkowników lub kombinacji typów jednostek. Nie tylko to, otrzymujemy również wiele DTO.
Poniższa odpowiedź to przykładowa implementacja Filtered Repository bez tworzenia całego zestawu klas i metod. Może nie odpowiadać bezpośrednio na pytanie, ale może być przydatne w wyprowadzeniu jednego.
Zastrzeżenie: Jestem autorem Entity REST SDK.
http://entityrestsdk.codeplex.com
Mając powyższe na uwadze, opracowaliśmy SDK, który tworzy repozytorium przefiltrowanego widoku w oparciu o SecurityContext, który zawiera filtry dla operacji CRUD. Tylko dwa rodzaje reguł upraszczają skomplikowane operacje. Pierwszy to dostęp do jednostki, a drugi to reguła odczytu / zapisu dla właściwości.
Zaletą jest to, że nie przepisujesz logiki biznesowej ani repozytoriów dla różnych typów użytkowników, po prostu blokujesz lub przyznajesz im dostęp.
public class DefaultSecurityContext : BaseSecurityContext { public static DefaultSecurityContext Instance = new DefaultSecurityContext(); // UserID for currently logged in User public static long UserID{ get{ return long.Parse( HttpContext.Current.User.Identity.Name ); } } public DefaultSecurityContext(){ } protected override void OnCreate(){ // User can access his own Account only var acc = CreateRules<Account>(); acc.SetRead( y => x=> x.AccountID == UserID ) ; acc.SetWrite( y => x=> x.AccountID == UserID ); // User can only modify AccountName and EmailAddress fields acc.SetProperties( SecurityRules.ReadWrite, x => x.AccountName, x => x.EmailAddress); // User can read AccountType field acc.SetProperties<Account>( SecurityRules.Read, x => x.AccountType); // User can access his own Orders only var order = CreateRules<Order>(); order.SetRead( y => x => x.CustomerID == UserID ); // User can modify Order only if OrderStatus is not complete order.SetWrite( y => x => x.CustomerID == UserID && x.OrderStatus != "Complete" ); // User can only modify OrderNotes and OrderStatus order.SetProperties( SecurityRules.ReadWrite, x => x.OrderNotes, x => x.OrderStatus ); // User can not delete orders order.SetDelete(order.NotSupportedRule); } }
Te reguły LINQ są oceniane względem bazy danych w metodzie SaveChanges dla każdej operacji, a te reguły działają jako zapora przed bazą danych.
źródło
Jest wiele dyskusji na temat tego, która metoda jest poprawna, więc patrzę na to, ponieważ obie są dopuszczalne, więc używam zawsze tej, która mi się najbardziej podoba (co nie jest repozytorium, UoW).
W EF UoW jest zaimplementowana za pośrednictwem DbContext, a DbSets to repozytoria.
Jeśli chodzi o pracę z warstwą danych, po prostu pracuję bezpośrednio na obiekcie DbContext, dla złożonych zapytań wykonam metody rozszerzające dla zapytania, które można ponownie wykorzystać.
Myślę, że Ayende ma również kilka postów o tym, jak wyodrębnianie operacji CUD jest złe.
Zawsze tworzę interfejs i mój kontekst dziedziczy po nim, więc mogę używać kontenera IoC dla DI.
źródło
To, co najczęściej dotyczy EF, nie jest wzorcem repozytorium. Jest to wzorzec fasady (abstrakcja wywołań metod EF do prostszych, łatwiejszych w użyciu wersji).
EF jest tym, który stosuje wzorzec repozytorium (a także wzorzec jednostki pracy). Oznacza to, że EF jest tym, który wyodrębnia warstwę dostępu do danych, aby użytkownik nie miał pojęcia, że ma do czynienia z serwerem SQLServer.
I przy tym, większość „repozytoriów” na EF nie jest nawet dobrymi Fasadami, ponieważ po prostu mapują, całkiem prosto, na pojedyncze metody w EF, nawet do punktu, w którym mają te same sygnatury.
Zatem dwa powody zastosowania tego tak zwanego wzorca „repozytorium” do EF to umożliwienie łatwiejszego testowania i ustanowienie podzbioru wywołań „gotowych” do niego. Nieźle samo w sobie, ale najwyraźniej nie jest repozytorium.
źródło
Linq jest obecnie „Repozytorium”.
ISession + Linq już jest repozytorium i nie potrzebujesz ani
GetXByY
metod, aniQueryData(Query q)
uogólnień. Będąc trochę paranoikiem do używania DAL, nadal wolę interfejs repozytorium. (Z punktu widzenia łatwości konserwacji nadal musimy mieć trochę fasad w stosunku do określonych interfejsów dostępu do danych).Oto repozytorium, którego używamy - odłącza nas od bezpośredniego użycia nhibernate, ale zapewnia interfejs linq (jako dostęp ISession w wyjątkowych przypadkach, które ostatecznie podlegają refaktoryzacji).
class Repo { ISession _session; //via ioc IQueryable<T> Query() { return _session.Query<T>(); } }
źródło
Repository (czy jak ktoś wybiera się nazwać) w tej chwili jest dla mnie przede wszystkim o abstrahując dala warstwy trwałości.
Używam go w połączeniu z obiektami zapytań, więc nie mam połączenia z żadną konkretną technologią w moich aplikacjach. A także bardzo ułatwia testowanie.
Więc zwykle mam
public interface IRepository : IDisposable { void Save<TEntity>(TEntity entity); void SaveList<TEntity>(IEnumerable<TEntity> entities); void Delete<TEntity>(TEntity entity); void DeleteList<TEntity>(IEnumerable<TEntity> entities); IList<TEntity> GetAll<TEntity>() where TEntity : class; int GetCount<TEntity>() where TEntity : class; void StartConversation(); void EndConversation(); //if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository. TResult ExecuteQuery<TResult>(IQueryObject<TResult> query); }
Ewentualnie dodaj metody asynchroniczne z wywołaniami zwrotnymi jako delegaty. Repo jest łatwa do wdrożenia rodzajowo , więc nie jestem w stanie dotknąć linię realizacji z aplikacji do aplikacji. Cóż, jest to prawda przynajmniej przy używaniu NH, zrobiłem to również z EF, ale sprawiło, że nienawidziłem EF. 4. Rozmowa jest początkiem transakcji. Bardzo fajnie, jeśli kilka klas współdzieli instancję repozytorium. Również w przypadku NH jedno repozytorium w mojej implementacji równa się jednej sesji otwieranej przy pierwszym żądaniu.
Następnie Query Objects
public interface IQueryObject<TResult> { /// <summary>Provides configuration options.</summary> /// <remarks> /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository. /// If not used through a repository, it can be useful as a configuration option. /// </remarks> void Configure(object parameter); /// <summary>Implementation of the query.</summary> TResult GetResult(); }
Dla konfiguracji używam w NH tylko do przejścia w ISession. W EF nie ma sensu mniej więcej.
Przykładowe zapytanie to… (NH)
public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>> where TEntity : class { public override IList<TEntity> GetResult() { return this.Session.CreateCriteria<TEntity>().List<TEntity>(); } }
Aby wykonać kwerendę EF, musisz mieć kontekst w bazie abstrakcyjnej, a nie w sesji. Ale oczywiście ifc byłby taki sam.
W ten sposób zapytania są same hermetyzowane i łatwe do przetestowania. A co najlepsze, mój kod opiera się tylko na interfejsach. Wszystko jest bardzo czyste. Obiekty domeny (biznesowe) są po prostu tym, że np. Nie ma mieszania obowiązków, jak przy użyciu wzorca aktywnego rekordu, który jest trudny do przetestowania i miesza kod dostępu do danych (zapytania) w obiekcie domeny, robiąc to, mieszając obawy (obiekt, który pobiera samo??). Każdy może nadal tworzyć POCO do przesyłania danych.
Podsumowując, takie podejście zapewnia wiele możliwości ponownego wykorzystania i prostoty kodu, przy czym nic nie mogę sobie wyobrazić. Jakieś pomysły?
Wielkie dzięki dla Ayende za jego świetne posty i nieustające poświęcenie. To jego pomysły tutaj (obiekt zapytania), nie moje.
źródło
Dla mnie to prosta decyzja, składająca się ze stosunkowo niewielu czynników. Czynniki to:
Tak więc, jeśli moja aplikacja nie może uzasadnić # 2, oddzielnego modelu domeny i danych, zwykle nie będę zawracać sobie głowy numerem 5.
źródło