10

Implementuję framework DAL wykorzystujący encje. W naszej aplikacji mamy trzy warstwy (DAL, warstwę biznesową i prezentację). To jest aplikacja internetowa. Kiedy rozpoczęliśmy wdrażanie DAL, nasz zespół uważał, że DAL powinien mieć klasy, których metody otrzymują ObjectContext podany przez usługi w warstwie biznesowej i działają na nim. Uzasadnieniem tej decyzji jest to, że różne ObjectContexts widzą różne stany DB, więc niektóre operacje mogą zostać odrzucone z powodu problemów z dopasowaniem obcych kluczy i innych niespójności.Czy wzór DTO plus UnitOfWork to dobre podejście do projektowania DAL dla aplikacji WWW?

Zauważyliśmy, że generowanie i propagowanie kontekstu obiektów z warstwy usług generuje duże powiązanie między warstwami. Dlatego zdecydowaliśmy się użyć DTO odwzorowanych przez Automapper (nie niezarządzane podmioty lub samo-śledzące podmioty argumentujące o wysokim sprzężeniu, wystawiające jednostki na wyższe warstwy i niską efektywność) i UnitOfWork. Oto moje pytania:

  1. Czy to jest właściwe podejście do projektowania DAL aplikacji WWW? Czemu?
  2. Jeśli odpowiedź "tak" na 1., w jaki sposób należy pogodzić koncepcję DTO z wzorcami UnitOfWork?
  3. Jeśli odpowiedź "nie" na 1., co może być poprawnym podejściem do projektowania DAL dla aplikacji sieci Web?

Proszę, o ile to możliwe, proszę podać bibliografię wspierającą odpowiedź.

o aktualnym wzorem:

Aplikacja została planowanego być opracowane na trzech warstwach: prezentacja, biznesu i DAL. Warstwa biznesowa ma zarówno fasady, jak i usługi.

Istnieje interfejs o nazwie ITransaction (zawierający tylko dwie metody usuwania i zapisywania zmian) widoczny tylko w usługach. Aby zarządzać transakcją, istnieje transakcja klasy rozszerzająca ObjectContext i ITransaction. Zaprojektowaliśmy to mając na uwadze, że w warstwie biznesowej nie chcemy, aby inne metody ObjectContext były dostępne.

W DAL, stworzyliśmy abstrakcyjne repozytorium za pomocą dwóch typów generycznych (jeden dla encji i drugi dla powiązanego DTO). To repozytorium ma metody CRUD zaimplementowane w sposób ogólny i dwie ogólne metody mapowania DTO i jednostek ogólnego repozytorium za pomocą AutoMappera. Konstruktor abstrakcyjnego repozytorium przyjmuje argument ITransaction jako argument i oczekuje, że ITransaction będzie obiektem ObjectContext w celu przypisania go do jego właściwości ObjectContext.

Konkretne repozytoria powinny tylko odbierać i zwracać typy .net i DTO.

Mamy teraz do czynienia z tym problemem: ogólna metoda tworzenia nie generuje tymczasowego lub trwałego identyfikatora dla dołączonych podmiotów (dopóki nie użyjemy SaveChanges(), a tym samym zerwania transakcji, którą chcemy); to oznacza, że ​​metody serwisowe nie mogą go używać do kojarzenia DTO w BL)

Odpowiedz

7

Jest wiele rzeczy, które się tu dzieje ... Założę się, że używasz architektury trójwarstwowej. To powiedziawszy, jestem niejasny w sprawie kilku decyzji projektowych, które podjąłeś i jakie były motywacje za ich zrobieniem. Ogólnie rzecz biorąc, powiedziałbym, że twój ObjectContext nie powinien być przekazywany w twoich klasach. Powinna istnieć jakaś klasa menedżera lub repozytorium, która obsługuje zarządzanie połączeniami. To rozwiązuje problem zarządzania stanem DB. Uważam, że wzór repozytorium działa tutaj bardzo dobrze. Stamtąd powinieneś być w stanie dość łatwo wdrożyć schemat jednostki pracy, ponieważ zarządzanie połączeniami będzie obsługiwane w jednym miejscu.Biorąc pod uwagę to, co wiem o twojej architekturze, powiedziałbym, że powinieneś używać strategii POCO. Używanie POCO nie wiąże cię ściśle z żadnym dostawcą ORM. Zaletą jest to, że twoje POCO będą mogły wchodzić w interakcje z twoim ObjectContextem (prawdopodobnie za pośrednictwem repozytorium jakiegoś rodzaju), co da ci wgląd w śledzenie zmian. Ponownie, stamtąd będzie można wdrożyć wzór Jednostki Pracy (transakcji), aby uzyskać pełną kontrolę nad tym, jak powinna zachowywać się transakcja biznesowa. Uważam, że jest to niezwykle przydatny artykuł wyjaśniający, jak to wszystko pasuje do siebie. Kod jest błędny, ale dokładnie ilustruje najlepsze praktyki dla typu opisywanej architektury: Repository, Specification and Unit of Work Implementation

Krótka wersja mojej odpowiedzi na pytanie numer 1 brzmi "nie". Powyższy link zapewnia to, co uważam za lepsze podejście dla ciebie.

+0

proszę sprawdzić ostatnią wersję pytania ... dzięki! – JPCF

+0

znakomity link ...! – JPCF

2

Powinieneś rzucić okiem na to, co dependency injection i inwersja sterowania w ogólności. Zapewniłoby to możliwość kontrolowania cyklu życia ObjectContext "z zewnątrz". Możesz zapewnić, że tylko 1 instancja kontekstu obiektowego jest używana dla każdego żądania http. Aby uniknąć ręcznego zarządzania zależnościami, polecam używanie kontenera jako StructureMap.

Inną przydatną (ale dość trudną i trudną do zrobienia) techniką jest abstrakcja wytrwałości. Zamiast bezpośrednio używać ObjectContext, należy użyć tak zwanego Repository, który jest odpowiedzialny za udostępnienie kolekcji podobnej do API dla magazynu danych. Zapewnia to użyteczne seam, którego można użyć do przełączania podstawowego mechanizmu przechowywania danych lub do wyłuszczania trwałości całkowicie w celu przeprowadzenia testów.

Jak już sugerował Jason - powinieneś także użyć POCO`s (zwykłe stare obiekty clr). Mimo to nadal istnieje niejawne powiązanie z strukturą podmiotu Powinieneś być świadomy, że jest o wiele lepszy niż używanie generowanych klas.

rzeczy, które może nie znaleźć gdzie indziej dość szybko:

  1. Postaraj się unikać używania unit of work. Twój model powinien definiować granice transakcji.
  2. Staraj się unikać korzystania z generic repositories (należy zwrócić uwagę na temat IQueryable).
  3. Nie jest wymagane, aby spamować Twój kod za pomocą repository pattern name.

Możesz także przeczytać o domain driven design. Pomaga radzić sobie ze złożoną logiką biznesową i daje świetne wytyczne, aby uczynić kod mniej proceduralny, bardziej zorientowany na obiekt.

+0

proszę sprawdzić ostatnią wersję pytania ... dzięki! – JPCF

+0

Czy masz przykład DDD z EF - szczególnie interesuje mnie sposób definiowania granic transakcyjnych. –

+0

@Ladislav niestety nie jestem zbyt zaznajomiony z EF. uprzejmie ślepo wierząc, że NHibernate jest wciąż lepszy. ale to zmieniłoby tylko część repozytorium. granice transakcji są rysowane przez same zagregowane korzenie, ponieważ są odpowiedzialne za prawidłowy stan i są utrzymywane w operacji atomowej. właściwe ich modelowanie jest kluczowe. –

1

Skupię się na bieżących problemach: Szczerze mówiąc, nie sądzę, że powinieneś omijać swój ObjectContext. Myślę, że to doprowadzi do problemów. Zakładam, że kontroler lub usługa biznesowa przejdzie ObjectContext/ITransaction do repozytorium. W jaki sposób zagwarantujesz, że Twój ObjectContext zostanie odpowiednio usunięty? Co dzieje się podczas korzystania z transakcji zagnieżdżonych? Co zarządza wycofywaniem transakcji w dół strumienia?

Wydaje mi się, że najlepszym rozwiązaniem jest wprowadzenie większej definicji tego, w jaki sposób można zarządzać transakcjami w swojej architekturze. Użycie TransactionScope w kontrolerze/usłudze jest dobrym początkiem, ponieważ ObjectContext respektuje to. Oczywiście może zajść potrzeba wzięcia pod uwagę, że kontrolery/usługi mogą nawiązywać połączenia z innymi kontrolerami/usługami, które mają w nich transakcje. Aby umożliwić scenariusze, w których chcesz mieć pełną kontrolę nad transakcjami biznesowymi i kolejnymi połączeniami z bazami danych, musisz utworzyć coś w rodzaju klasy TransactionManager, która rejestruje i generalnie zarządza transakcjami w górę i w dół stosu.Zauważyłem, że NCommon wykonuje niezwykłą pracę zarówno przy analizie transakcji, jak i ich zarządzaniu. Zobacz tam klasy UnitOfWorkScope i TransactionManager. Chociaż nie zgadzam się z podejściem NCommon polegającym na zmuszaniu repozytorium do polegania na UnitOfWork, to z łatwością można je refaktoryzować, jeśli chcesz.

Jeśli chodzi o Twój problem persistantID idzie, check this out

+0

Przede wszystkim, dzięki! wydajesz się być bardzo zainteresowany. Tworzymy transakcję w serwisach, ale korzystamy z ITransaction; ten interfejs ujawnia tylko dwie metody: Dispose and SaveChances. Obiekt transakcji to obiektowy kontekst, ale jest traktowany jak obiekt w DAL; usługi zobacz tylko ITransaction. Zapisywanie i usuwanie transakcji to odpowiedzialność programistów za metody obsługi. Nie implementujemy zagnieżdżonych transakcji i są one proste, więc nie rozważamy wycofywania. – JPCF

4

Zawsze wierzyłem, że kod może wyjaśnić rzeczy lepiej niż światów dla programistów. Dotyczy to szczególnie tego tematu. To dlatego sugeruję, abyś spojrzał na wspaniałą przykładową aplikację z wszystkimi oczekiwanymi podejściami.

alt text

Projekt nazywa Sharp Architecture, to jest wokół MVC i NHibernate, ale można korzystać z tych samych metod tylko zastępując NHibernate części z EF te, gdy są potrzebne. Celem tego projektu jest dostarczenie szablonu aplikacji ze wszystkimi najlepszymi praktykami społecznościowymi do tworzenia aplikacji internetowych.

Obejmuje ona wszystkie powszechne i większość nietypowych zagadnień przy użyciu ORM użytkownika, zarządzanie transakcjami, zarządzanie zależnościami z kontenerów IoC, wykorzystania DTOs itp

A oto sample application.

Nalegam na czytanie i wypróbowanie tego, będzie to dla ciebie prawdziwe połączenie, jak to było dla mnie.

Powiązane problemy