Oddzielne schematy/bazy danych per kraj

0

Do tej pory programowałem w PHP i tam sprawa była prosta, ponieważ cała aplikacja ma request scope. Co request tworzy się nowe połączenie do bazy danych, więc wystarczyło za każdym razem wczytać odpowiedni config na podstawie domeny i voilà - mamy połączenie z odpowiednią bazą.

Z tego co wiem, to PHP jest pod tym względem wyjątkiem. W C#, Javie i innych aplikacja cały czas działa. Mamy za to do dyspozycji request scope, session scope i inne, jeżeli chodzi o serwisy. W jaki sposób byście to zrobili? Technologie jak w tagach.

2

Z tego co wiem, to PHP jest pod tym względem wyjątkiem. W C#, Javie i innych aplikacja cały czas działa.

Masz rację w takim języku jak C# aplikacje działają, nie to co w PHP. W C# też masz request scope i możesz nawiązać connection z bazą per request. Najłatwiej to ogarnąć jakimś kontenerem DI.

0

No właśnie, ciekawy byłem, czy zrobienie tego przez request scope jest prawidłowym rozwiązaniem. Wiadomo, zawsze można coś wykombinować, ale ponieważ nie mam jeszcze za dużej wiedzy na ten temat, to chciałem potwierdzić swoje przypuszczenia. Zwłaszcza, że inni programiści, z którymi rozmawiałem na ten temat mówili, że jest to trudne/niewykonalne.

1

Takie rozwiązanie moim zdaniem będzie ok.
EDIT: Ale co jest nie wykonalne?

0

Podobno nie mogli tego zrobić i padło takie słowo, ale u mnie działa ;)

2

Nie bardzo wiem o czym wy tutaj rozmawiacie, chcecie mieć otwarte połączenie do bazy przez cały czas trwania requesta ? Po co?
Powszechnym zaleceniem jest trzymanie otwartego połączenia jak najkrócej się tylko da, bo liczba możliwych jednoczesnych połączeń jest ograniczona. Połączenia są brane z puli, więc otwieranie i zamykanie połączeń jest znacznie lepszym pomysłem niż trzymanie otwartego połączenia przez cały request.

0

Nie, zupełnie nie o to mi chodziło. Zgadzam się z Twoim stwierdzeniem. Miałem na myśli połączenie z konkretną baza lub odwolanie do konkretnego schematu bazując lokalizacyjnych z requestu.

0

@neves: to czemu domyślnie EF ma ustawiony DbContext na scoped? Może mieszam pojęcia i DbContext to coś innego niż połączenie. Póki co chodzę troche jak dziecko we mgle.

Chodziło o to, żeby używać różnych baz w zależności od języka (wyciąganego z HttpContext). Gość z którym rozmawiałem zna .net'a dłużej niż ja (ja znam go całe dwa tygodnie..) i miał problem ze skonfigurowaniem tego. Do tej pory robiłem w PHP, a tam rzeczy są robione na różne dziwne sposoby i chciałem się upewnić, że to rozwiązanie, które znalazłem jest okej, a wygląda to tak:

 var connectionString = _config.GetConnectionString("EntityContext");

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped(p => p.GetService<IHttpContextAccessor>()?.HttpContext);
            
services.AddEntityFrameworkNpgsql()
    .AddDbContext<EntityContext>(
        (serviceProvider, options) => {
            var httpContext = serviceProvider.GetService<HttpContext>();
            // here we can get language from header or whatever
            var locale = httpContext.Request.Query["locale"].ToString();
                        
            options
                .UseLazyLoadingProxies()
                .UseNpgsql(String.Format(connectionString, locale));
        });
1

DbContext to nie to samo co połączenie, DbContext to UnitOfWork które samo zarządza otwieraniem i zamykaniem połączeń wtedy kiedy potrzebuje i nie prześlemy mu jakiegoś explicite.

Jak mamy db context per request, to może się zdarzyć że w dwóch miejscach będziemy mieć wstrzykniętą tą samą instancję dbcontexta, to pierwsze miejsce może porobić na nim różne rzeczy bez zapisywania zmian, a to drugie miejsce być tego nieświadome i dostać zabrudzony dbcontext, dlatego jest bezpieczniej jednak ustawiać dbcontext jako Transient.

Inaczej też ujmując jak mamy dbcontext per request, to nigdy nie mamy pewności że dostajemy czysty dbcontext bez żadnych wprowadzanych i nie zapisanych zmian z innego fragmentu kodu, które możemy nieświadomie teraz zapisać , nie sprzyja to zdecydowanie izolacji i przejrzystości.

A kod wygląda jak najbardziej poprawnie ;)

1
neves napisał(a):

DbContext to nie to samo co połączenie, DbContext to UnitOfWork które samo zarządza otwieraniem i zamykaniem połączeń wtedy kiedy potrzebuje i nie prześlemy mu jakiegoś explicite.

Jak mamy db context per request, to może się zdarzyć że w dwóch miejscach będziemy mieć wstrzykniętą tą samą instancję dbcontexta, to pierwsze miejsce może porobić na nim różne rzeczy bez zapisywania zmian, a to drugie miejsce być tego nieświadome i dostać zabrudzony dbcontext, dlatego jest bezpieczniej jednak ustawiać dbcontext jako Transient.

Inaczej też ujmując jak mamy dbcontext per request, to nigdy nie mamy pewności że dostajemy czysty dbcontext bez żadnych wprowadzanych i nie zapisanych zmian z innego fragmentu kodu, które możemy nieświadomie teraz zapisać , nie sprzyja to zdecydowanie izolacji i przejrzystości.

A kod wygląda jak najbardziej poprawnie ;)

Hmm.. argument że ktoś coś zmienił u nie zapisał do mnie nie trafia i brzmi jak jakiś code smell, chyba że miałeś coś innego na myśli. Przy rejestracji transient I używaniu contextu w dwóch różnych miejscach tracisz właśnie korzyści Unit of work ponieważ gdy zmienisz coś w jednym serwisie a nastepnie w drugim serwisie wstrzykniesz nowy dbcontext to gdy coś pójdzie nie tak to nie jesteś w stanie w prosty sposób wycofać pierwszej operacji bo tam uzywales już innego contextu. W większości przypadków porządane jest aby przy jednym żądaniu http wykonały się wszystkie operacje a nie połowa. Drugą sprawą mniej ważna szczególnie jeśli chodzi o EF jest cache który jest czyszczony przy każdym nowym dbcontexcie.

0
error91 napisał(a):

Hmm.. argument że ktoś coś zmienił u nie zapisał do mnie nie trafia i brzmi jak jakiś code smell, chyba że miałeś coś innego na myśli. Przy rejestracji transient I używaniu contextu w dwóch różnych miejscach tracisz właśnie korzyści Unit of work ponieważ gdy zmienisz coś w jednym serwisie a nastepnie w drugim serwisie wstrzykniesz nowy dbcontext to gdy coś pójdzie nie tak to nie jesteś w stanie w prosty sposób wycofać pierwszej operacji bo tam uzywales już innego contextu. W większości przypadków porządane jest aby przy jednym żądaniu http wykonały się wszystkie operacje a nie połowa. Drugą sprawą mniej ważna szczególnie jeśli chodzi o EF jest cache który jest czyszczony przy każdym nowym dbcontexcie.

Masz dwa serwisy S1, S2 do których wstrzykujesz bezpośrednio ten sam dbContext, i chcesz mieć je w jednej biznesowej transakcji. Gdzie wywołujesz saveChanges :)?
A co jeśli kolejność wywołania serwisów się zmieni?
A co jak dodamy kolejny serwis wywoływany przed nimi S3 który nie wchodzi w skład powyższej biznesowej transakcji, SaveChanges rzuci błędem, a nasze serwisy S1 S2 dostaną ten sam datacontext :)?

0

To zależy. W większości przypadków SaveChanges wywolam w każdym serwisie bo mogę np potrzebować id zapisanej encji. Stworze dodatkowy serwis który zepnie transakcje na s1 i s2. W tym przypadku jeśli serwisy będą niezależne kolejność nie ma znaczenia. Jeśli rozmawiamy o webie to w przypadku dodania s3 musi on wejść w transakcje bo jest wolany w tym samym requescie. Innym rozwiązaniem moze być rzucenie eventu i zasubskrybowanie s3 na ten event i ewentualna kompensacja s1 i s2 przy bledzie. Jeśli s3 rzuci błędem to s1 i s2 się nie wykona a transakcja zostanie wycofana. Twoim zdaniem gdy s3 rzuci błędem (będąc w transakcji) to s1 i s2 dostaną świeży context I zrobią swoją robotę? To doprowadzi to niespójności bazy danych.

0
error91 napisał(a):

To zależy. W większości przypadków SaveChanges wywolam w każdym serwisie bo mogę np potrzebować id zapisanej encji. Stworze dodatkowy serwis który zepnie transakcje na s1 i s2. W tym przypadku jeśli serwisy będą niezależne kolejność nie ma znaczenia.

s1 robi update, s2 też robi update, czy rozsądnym jest wykorzystanie do tego transakcji otwieranej na bazie danych skoro nasz dbcontext oferuje nam UoW?
zresztą transakcje można otwierać pomiędzy różnymi instancjami dbcontext, a Twój główny argument za współdzieleniem dbcontext był taki że wtedy możemy w pełni wykorzystać UoW :D

error91 napisał(a):

Jeśli rozmawiamy o webie to w przypadku dodania s3 musi on wejść w transakcje bo jest wolany w tym samym requescie.

A co gdy jednak chcemy mieć możliwość wykonywania wielu transakcji w jednym requescie ;)?

error91 napisał(a):

Twoim zdaniem gdy s3 rzuci błędem (będąc w transakcji) to s1 i s2 dostaną świeży context I zrobią swoją robotę? To doprowadzi to niespójności bazy danych.

s3 jak już pisałem jest kompletnie niezależną transakcją od s1 i s2, jakby miało to doprowadzić do niespójności to by wszystkie były w jednej transakcji.

0

s1 robi update, s2 też robi update, czy rozsądnym jest wykorzystanie do tego transakcji otwieranej na bazie danych skoro nasz dbcontext oferuje nam UoF?
zresztą transakcje można otwierać pomiędzy różnymi instancjami dbcontext, a Twój główny argument za współdzieleniem dbcontext był taki że wtedy możemy w pełni wykorzystać UoF :D

Napisałem przecież że to zależy? Jeśli operacje są proste i sięgają 2-3 tabelek to ok nie ma sensu. Ale gdy operacja dotyka wielu tabel to wole to podzielić na oddzielne klasy żeby kod był czytelniejszy I łatwiej modyfikowany. W innym wypadku skończysz z metodą ktora ma 500 lini i nic nie widać co się dzieje. Druga sprawa to pobranie id dodanej encji. Bez transakcji po savechanges encja bedzie już możliwa do odczytu A to prowadzi do dirty readow. Poza tym transaction scope to też jest unit of work.

A co gdy jednak chcemy mieć możliwość wykonywania wielu transakcji w jednym requescie ;)?

s3 jak już pisałem jest kompletnie niezależną transakcją od s1 i s2, jakby miało to doprowadzić do niespójności to by wszystkie były w jednej transakcji.

Jeśli operacje mają być nie powiązane to czemu je wołać w jednym requescie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1