Obecnie używamy Entity Framework jako ORM w kilku aplikacjach internetowych i do tej pory nam to odpowiadało, ponieważ wszystkie nasze dane są przechowywane w jednej bazie danych. Używamy wzorca repozytorium i mamy usługi (warstwa domeny), które z nich korzystają, i zwracają jednostki EF bezpośrednio do kontrolerów ASP.NET MVC.
Jednak pojawił się wymóg korzystania z zewnętrznego interfejsu API (za pośrednictwem usługi internetowej), który zapewni nam dodatkowe informacje dotyczące użytkownika w naszej bazie danych. W naszej lokalnej bazie danych użytkowników będziemy przechowywać zewnętrzny identyfikator, który możemy podać API, aby uzyskać dodatkowe informacje. Dostępnych jest sporo informacji, ale dla uproszczenia jedna z nich dotyczy firmy użytkownika (imię i nazwisko, kierownik, pokój, stanowisko, lokalizacja itp.). Informacje te będą wykorzystywane w różnych miejscach w naszych aplikacjach internetowych, a nie w jednym miejscu.
Więc moje pytanie brzmi: gdzie jest najlepsze miejsce do wypełnienia tych informacji? Ponieważ jest używany w różnych miejscach, nie jest sensowne pobieranie go na zasadzie ad hoc, gdziekolwiek korzystamy z aplikacji internetowej - więc warto zwrócić te dodatkowe dane z warstwy domeny.
Początkowo myślałem o stworzeniu klasy modelu opakowania, która zawierałaby encję EF (EFUser) i nową klasę „ApiUser” zawierającą nowe informacje - a kiedy dostajemy użytkownika, otrzymujemy EFUser, a następnie otrzymujemy dodatkowe informacje z interfejsu API i wypełnij obiekt ApiUser. Jednak chociaż byłoby to dobre w przypadku pozyskiwania pojedynczych użytkowników, przewraca się, gdy uzyskuje się wielu użytkowników. Nie możemy trafić do interfejsu API podczas uzyskiwania listy użytkowników.
Moją drugą myślą było po prostu dodanie metody singleton do encji EFUser, która zwraca ApiUser, i po prostu zapełnienie jej w razie potrzeby. To rozwiązuje powyższy problem, ponieważ uzyskujemy do niego dostęp tylko wtedy, gdy jest to potrzebne.
Lub ostatnią myślą było zachowanie lokalnej kopii danych w naszej bazie danych i zsynchronizowanie jej z interfejsem API, gdy użytkownik się zaloguje. Jest to minimalna praca, ponieważ jest to tylko proces synchronizacji - i nie mamy narzutu związanego z uderzeniem DB i API za każdym razem, gdy chcemy uzyskać informacje o użytkowniku. Oznacza to jednak przechowywanie danych w dwóch miejscach, a także oznacza, że dane są nieaktualne dla każdego użytkownika, który nie logował się przez dłuższy czas.
Czy ktoś ma jakieś porady lub sugestie, jak najlepiej poradzić sobie z tego rodzaju scenariuszem?
źródło
it's not really sensible to fetch it on an ad-hoc basis
-- Dlaczego? Ze względu na wydajność?Odpowiedzi:
Twój przypadek
W twoim przypadku wszystkie trzy opcje są wykonalne. Myślę, że najlepszą opcją jest prawdopodobnie zsynchronizowanie źródeł danych w miejscu, którego aplikacja asp.net nawet nie zna. Oznacza to, że za każdym razem unikaj dwóch pobrań na pierwszym planie, po cichu synchronizuj interfejs API z db). Więc jeśli jest to realna opcja w twoim przypadku - mówię, zrób to.
Rozwiązanie, w którym pobieranie odbywa się „raz”, jak sugeruje inna odpowiedź, nie wydaje się zbyt opłacalne, ponieważ nie utrzymuje żadnej odpowiedzi w dowolnym miejscu, a ASP.NET MVC po prostu pobiera pobieranie dla każdego żądania w kółko.
Unikałbym singletonu, nie sądzę, że to dobry pomysł z wielu typowych powodów.
Jeśli trzecia opcja nie jest wykonalna - jedną z opcji jest leniwe załadowanie jej. Oznacza to, że klasa musi rozszerzyć encję i niech trafi do interfejsu API w razie potrzeby . To bardzo niebezpieczna abstrakcja, ponieważ jest jeszcze bardziej magiczna i nieoczywista.
Chyba sprowadza się to do kilku pytań:
Kilka słów o rozdzieleniu obaw
Pozwólcie mi spierać się z tym, co Bobson mówi o rozdzieleniu obaw tutaj. Ostatecznie - umieszczenie tej logiki w takich bytach narusza tak samo rozdzielenie obaw.
Posiadanie takiego repozytorium narusza podział problemów równie źle, poprzez umieszczenie logiki centrycznej prezentacji w warstwie logiki biznesowej. Twoje repozytorium nagle zdaje sobie sprawę z rzeczy związanych z prezentacją, takich jak sposób wyświetlania użytkownika w kontrolerach asp.net mvc.
W tym powiązanym pytaniu zadałem pytanie o dostęp do encji bezpośrednio z kontrolera. Pozwól mi zacytować jedną z odpowiedzi:
(Przeczytaj resztę odpowiedzi, to naprawdę ładne imo).
Naiwnością jest ignorowanie faktu, że istnieje baza danych - istnieje baza danych i bez względu na to, jak bardzo chcesz ją wyodrębnić, nigdzie się nie zmierza. Twoja aplikacja będzie wiedziała o źródle danych. Nie będziesz w stanie „zamienić go na gorąco”. ORM są przydatne, ale przeciekają ze względu na to, jak skomplikowany jest problem, który rozwiązują oraz z wielu powodów związanych z wydajnością (na przykład Select n + 1).
źródło
Przy odpowiednim rozdzieleniu problemów nic powyżej poziomu Entity Framework / API nie powinno nawet wiedzieć, skąd pochodzą dane. O ile wywołanie interfejsu API nie jest drogie (pod względem czasu lub przetwarzania), dostęp do danych, które go wykorzystują, powinien być tak przejrzysty, jak dostęp do danych z bazy danych.
Najlepszym sposobem na wdrożenie tego byłoby dodanie do
EFUser
obiektu dodatkowych właściwości, które w razie potrzeby leniwie ładują dane API. Coś takiego:Zewnętrznie, po raz pierwszy albo
CompanyName
czyJobTitle
jest używany nie będzie pojedynczy wywołanie API wykonany (a zatem małe opóźnienie), ale wszystkie kolejne połączenia, dopóki obiekt jest niszczony będzie tak szybkie i proste jak dostęp do bazy danych.źródło
Jednym z pomysłów jest modyfikacja ApiUser, aby nie zawsze zawierała dodatkowe informacje. Zamiast tego umieścisz metodę na ApiUser, aby ją pobrać:
Możesz to również nieco zmodyfikować, aby użyć leniwego ładowania dodatkowych danych, abyś nie musiał wyodrębniać UserExtraData z obiektu ApiUser:
W ten sposób, gdy masz listę, dodatkowe dane nie będą domyślnie pobierane. Ale nadal możesz uzyskać do niego dostęp podczas przeglądania listy!
źródło