Entity Framework z dużymi systemami - jak podzielić modele?

50

Pracuję z bazą danych SQL Server z ponad 1000 tabel, jeszcze kilkaset widoków i kilka tysięcy procedur przechowywanych. Chcemy zacząć korzystać z Entity Framework w naszych nowszych projektach i pracujemy nad naszą strategią. To, na czym się rozłączam, polega na tym, jak najlepiej podzielić tabele na różne modele (EDMX lub DbContext, jeśli najpierw uruchomimy kod). Mogę od razu wymyślić kilka strategii:

  • Podzielone według schematu
    Mamy tabele podzielone na prawdopodobnie kilkanaście schematów. Możemy wykonać jeden model na schemat. Nie jest to jednak idealne, ponieważ dbo wciąż jest bardzo duże, z ponad 500 tabelami / widokami. Innym problemem jest to, że niektóre jednostki pracy będą musiały dokonywać transakcji obejmujących wiele modeli, co zwiększa złożoność, chociaż zakładam, że EF czyni to dość prostym.
  • Podziel według zamiarów
    Zamiast martwić się o schematy, podziel modele według zamiarów. Będziemy więc mieć różne modele dla każdej aplikacji, projektu, modułu, ekranu, w zależności od tego, jak bardzo chcemy uzyskać szczegółowość. Problem, jaki tu widzę, polega na tym, że istnieją pewne tabele, które nieuchronnie muszą być używane w każdym przypadku, takie jak Użytkownik lub AuditHistory. Czy dodajemy je do każdego modelu (chyba narusza DRY), czy też są w osobnym modelu, który jest używany w każdym projekcie?
  • Nie dziel się wcale - jeden gigantyczny model
    Jest to oczywiście proste z punktu widzenia rozwoju, ale z moich badań i mojej intuicji wydaje się, że może on działać strasznie, zarówno w czasie projektowania, podczas kompilacji, jak i w czasie wykonywania.

Jaka jest najlepsza praktyka używania EF przeciwko tak dużej bazie danych? W szczególności, jakie strategie stosują ludzie przy projektowaniu modeli w stosunku do tej liczby obiektów DB? Czy są opcje, o których nie myślę, że działają lepiej niż to, co mam powyżej?

Czy jest to również problem w przypadku innych ORM, takich jak NHibernate? Jeśli tak, to czy wymyślili jakieś lepsze rozwiązania niż EF?

RationalGeek
źródło
„konieczność wykonywania transakcji obejmujących wiele modeli, co zwiększa złożoność”. Wystarczy pamiętać, że należy włączyć koordynatora transakcji rozproszonych firmy Microsoft. Kiedy już to uruchomisz, powinno być łatwo osiągnąć to, o czym mówisz.
Tjaart
@Tjaart dzięki. Używałem MS DTC wcześniej i chociaż jest to dość proste, dodaje złożoności poza prostą DB txn, więc chcę tego uniknąć, gdy tylko jest to możliwe.
RationalGeek,
2
4 lata później, co zdecydowałeś i co byś teraz polecił?
Rory,

Odpowiedzi:

31

Osobiście próbowałem stworzyć jeden ogromny schemat dla wszystkich moich bytów w dość złożonym, ale małym projekcie (~ 300 tabel). Mieliśmy bardzo znormalizowaną bazę danych (normalizacja 5. formy (mówię to luźno)) z wieloma relacjami „wiele do wielu” i ekstremalnym wymuszaniem integralności referencyjnej.

Zastosowaliśmy także strategię „pojedynczej instancji na żądanie”, która nie jest przekonana, że ​​pomogła.

Podczas wykonywania prostych, stosunkowo płaskich „wyraźnie zdefiniowanych” list, wyszukiwania i zapisywanie wydajności było ogólnie akceptowalne. Ale kiedy zaczęliśmy wnikać w głębokie relacje, występ wydawał się gwałtownie spadać. W porównaniu do przechowywanego proc w tym przypadku nie było porównania (oczywiście). Jestem pewien, że moglibyśmy ulepszyć bazę kodu tu i tam, aby poprawić wydajność, jednak w tym przypadku potrzebowaliśmy tylko zwiększenia wydajności bez analizy ze względu na ograniczenia czasowe, i wróciliśmy do przechowywanego proc (wciąż go zmapowano) poprzez EF, ponieważ EF dawał mocno typowane wyniki), potrzebowaliśmy tego tylko jako cofnięcia w kilku obszarach. Kiedy musieliśmy przeglądać całą bazę danych, aby utworzyć kolekcję (bez oszczędnego używania .include ()), wydajność wyraźnie spadła, ale może chcieliśmy za dużo ...

Tak więc na podstawie mojego doświadczenia zaleciłbym utworzenie osobnego pliku .edmx dla każdego celu. Generuj tylko to, czego będziesz używać, na podstawie zakresu tej potrzeby. Możesz mieć kilka mniejszych plików .edmx do zadań specjalnych, a następnie kilka dużych, w których musisz przechodzić złożone relacje, aby budować obiekty. Nie jestem pewien, gdzie jest to magiczne miejsce, ale jestem pewien, że jest jeden ... lol ...

Szczerze mówiąc, oprócz kilku pułapek, które widzieliśmy (skomplikowane przechodzenie), ogromny plik .edmx działał dobrze z „działającej” perspektywy. Ale musisz uważać na magię „naprawiania”, jaką robi kontekst za sceną, jeśli nie wyłączasz jej jawnie. Oprócz utrzymywania synchronizacji pliku .edmx podczas wprowadzania zmian w bazie danych .. czasami łatwiej było wyczyścić całą powierzchnię i ponownie utworzyć obiekty, co zajęło około 3 minut, więc nie było to wielkim problemem.

Wszystko to było dzięki EntityFramework 4.1. Bardzo chciałbym usłyszeć o twoim ostatecznym wyborze i doświadczeniu.

A jeśli chodzi o twoje pytanie dotyczące nHibernate, moim zdaniem jest to pytanie o puszkę robaków, będziesz szczekał po obu stronach ogrodzenia ... Słyszę, że wiele osób walczy EF w celu walenia bez pracy przez wyzwania i zrozumienie niuansów charakterystycznych dla samej EF .. i chociaż nigdy nie korzystałem z nHibernate w produkcji, ogólnie rzecz biorąc, jeśli musisz ręcznie i jawnie tworzyć takie rzeczy jak mapowania, uzyskasz większą skończoną kontrolę, jeśli jednak mogę przeciągać i upuszczać, generować i uruchamiać CRUD'ing i kwerendy za pomocą LINQ, mogę dać bzdury na temat ziarnistości.

Mam nadzieję, że to pomoże.

hanzolo
źródło
1
Do Twojej wiadomości - istnieje narzędzie mapowania NHibernate, które sprawia, że ​​mapowania te są BARDZO łatwe i zautomatyzowane.
ganders
@ ganders - Czy ma interfejs użytkownika i jak to jest integracja z IDE? Zakładam, że wskazałeś źródło danych, które szanuje integralność referencyjną i przemierzanie obiektów oraz tworzy obiekty mapujące?
hanzolo,
1
Tak to robi (GUI). Do tej pory nie miałem z tym problemów. Używał go w 4 lub 5 różnych projektach / witrynach. Uwaga: używam go z Fluent NHibernate, który wykonuje mapowanie w kodzie c #, a nie w plikach config / xml. Oto link: nmg.codeplex.com
ganders
13

Zacznę od prostego wyjaśnienia: nie mam doświadczenia z tak dużą bazą danych, więc reszta mojej odpowiedzi nie jest oparta na przykładzie z prawdziwego świata.

Masz więc dużą bazę danych i chcesz jej używać z ORM / EF. Wybrałbym drugi wybór. Oto moje proste wyjaśnienie, dlaczego:

  • Mapowanie zwiększa złożoność. Nie ma potrzeby dodawania złożoności do encji, których twoja bieżąca aplikacja / projekt / moduł nigdy nie potrzebuje, ale nie obniżaj poziomu szczegółowości. Posiadanie osobnego zestawu mapowania na ekran również nie pomoże.
  • Chcesz osiągnąć jednostkę pracy. Powinieneś być w stanie określić, który moduł tabel potrzebuje w większości przypadków (nie jest konieczny we wszystkich przypadkach). Jeśli umieścisz te tabele w jednym zestawie mapowania, będziesz w stanie obsłużyć odczyt i modyfikację danych według pojedynczej instancji kontekstu - to powinien być twój ostateczny cel.
  • Nie jestem pewien, co dokładnie masz na myśli przez model, ale nawet przy różnych zestawach odwzorowań możesz dzielić klasy między zestawami odwzorowań przy użyciu tych samych typów jednostek. Jeśli więc używasz tabeli użytkowników w dwóch modułach, nie potrzebujesz dwóch klas użytkowników, aby reprezentować to samo. Nadal możesz używać pojedynczej tabeli, aw przypadku mapowania kodu (inaczej kod-pierwszy) możesz nawet zdefiniować mapowanie raz i załadować go do wielu zestawów mapowania, aby zasada DRY nie została naruszona, ale podejście do kodu ma więcej ograniczeń, jeśli chodzi do widoków i procedur przechowywanych. EDMX czyni to trudniejszym. Nadal możesz ponownie wykorzystywać klasy, ale ponowne użycie mapowania jest niemożliwe.
  • Co z zapytaniami między modułami? Te zapytania mogą się zdarzyć, ale szczerze mówiąc, nie wszystko musi być obsługiwane przez EF. Możesz skorzystać z EF dla typowych przypadków, aby uprościć regularny dostęp do danych, ale jeśli potrzebujesz gdzieś specjalnego zapytania, które łączy tabele należące do 5 różnych modułów, możesz po prostu wykonać go bezpośrednio lub zawinąć w procedurę przechowywaną. 100% zastąpienie rodzimego dostępu do danych może być trudne, złożone i przeciwwskazane.
  • Ostatni punkt jest po prostu praktyczny: nie wierzę, że oprzyrządowanie VS jest gotowe do pracy z tak dużym zestawem obiektów - nie w projektowaniu, nawet z narzędziem do importowania. Pracowałem nad bardzo dużą bazą danych z tradycyjnym dostępem do danych i projektem bazy danych SQL w VS2008 - wrażenia użytkownika związane ze złożonym projektem były bardzo złe. Liczba używanych tabel musi być niska - limit dla projektanta powinien wynosić między 100-200, ale nawet 100 tabel obsługiwanych przez jeden kontekst (zestaw mapowania) brzmi jak zbyt duża odpowiedzialność za jedną klasę (załóżmy, że będziesz mieć 100 właściwości zestawu uwidocznione w kontekście - nie wygląda to na dobry projekt).
Ladislav Mrnka
źródło
4

Powiedziałbym, że nie możesz zdecydować się na tego rodzaju pytanie z technicznego punktu widzenia. Radziłbym zbudować architekturę w oparciu o przypadki użycia (historie użytkowników itp.). Najpierw znajdź swoje obiekty biznesowe. Obiekt encji nie jest domyślnie obiektem biznesowym. Typowo będziesz mieć obiekt biznesowy przed obiektami encji. Następnie możesz stopniowo decydować, czego naprawdę potrzebujesz, na podstawie wymagań użytkownika.

„Dobry architekt maksymalizuje liczbę niepodejmowanych decyzji”. Robert C. Martin

http://cleancoder.posterous.com/architecture-deference

ollins
źródło
3

Używam podejścia hybrydowego - operacje OLTP są obsługiwane przez EF, podczas gdy ciężkie operacje, takie jak wstawianie partii, masowe aktualizacje, zapytania raportów itp. Są obsługiwane przez przechowywane procesy. Ułatwia także ścieżkę migracji, jeśli nie wykonujesz pełnego ponownego zapisu warstwy danych naraz.

Nik
źródło
To wydaje się być dobrą strategią, ale tak naprawdę nie rozwiązuje problemu, jak podzielić jednostki pomiędzy różne modele EF. Czy masz wszystkie byty w jednym modelu, czy dzielisz i podbijasz w jakiś sposób?
RationalGeek,
1
Jeśli wydajność OLTP jest wystarczająca w przypadku podejścia pełnego modelu, przejdź do tego. Zawsze możesz to zepsuć później, jeśli musisz, ale najszybszym i najbardziej zwinnym sposobem jest załadowanie całości. Być może nigdy nie będziesz potrzebować przyrostu wydajności, dzieląc go na części, marnując czas i komplikując system bez żadnego powodu. Następnie pojawia się pytanie, do którego modelu chcesz przykleić nową tabelę / jednostkę, kiedy zdecydujesz się rozwinąć. A co dzieje się, gdy trzeba uruchomić aktualizację dla wielu modeli. Zaoszczędź sobie bólu głowy, chyba że naprawdę nie masz alternatywy.
Nik
Zapomniałem wspomnieć, że zawsze możesz poprawić swoją wydajność podczas uzyskiwania dostępu do swoich danych. Przyjrzyj się leniwym / chętnym opcjom ładowania i tym, które elementy potomne sprowadzasz. Nie widzę powodu, dla którego pełny model zachowywałby się gorzej niż mniejszy, jeśli nie ładowałbyś masywnych drzew obiektów.
Nik
powiedziałbym, że ogromne drzewa obiektów i znormalizowana struktura danych idą w parze, gdy mamy do czynienia z dużymi schematami
hanzolo,
Ty decydujesz, jak mało lub ile chcesz nasycić wykres obiektów.
Nik