Norbert M.

Norbert M. Nobody's perfect.
Call me Nobody ;)

Temat: Połączenie z bazą

Jak według powinno się robić (i dlaczego):
- Czy powinno się przy każdej operacji na bazie otwierać i zamykać połączenie?
- Czy połączyć się raz na początki i zamknąć je przy zamykaniu aplikacji?
Czy też jest to od czegoś uzależnione??

Temat: Połączenie z bazą

Od tego, co chcesz uzyskać.

Dobrą szkołą jest zamykanie połączeń z bazą danych, ponieważ połączenie jest zasobem, a zasoby są ograniczone (korzystają z potoków, gniazd, slotów i innych zasobów, których jest w systemie ograniczona liczba). Zaleca się tu wykorzystanie z "using", wykonanie co potrzeba, zasilenie DataSetów, zamknięcie połączenia i pozostawienie "usingowi" zwolnienie zasobów (ew. można zamykanie przenieść samemu do destruktora, ale nie poleca się, m.in. z uwagi na problem wyjątków - znane jeszcze z książek o C++), a potem ew. merdżowanie DataSetów. NHibernate też pozwala pracować na "detached" obiektach. I też operuje pojęciem sesji. I też opisywane są różne "kejsy" w zależności od tego, co jest potrzebne.

Czasem musisz mieć na bieżąco dostęp do bazy ze względu na dynamiczne widoki, "wędrowanie okienkiem po długich listach" w przypadku, gdy dane w bazie mogą się nieustannie zmieniać. I wtedy nie chcesz ciągle otwierać połączenia i go zamykać z uwagi na czas.
A czasem pobierasz komplet danych do DataSetu, odłączasz się od bazy (w której dane zmieniają się rzadko) i działasz lokalnie, ew. tylko potem synchronizując oba zbiory danych (DataSet, baza).

W jednych aplikacjach dostęp do bazy jest zawsze (pomijając awarie sieci), a niektóre oferują tryb pracy offline i muszą "żonglować" połączeniami i przechowywać lokalnie dane (np. jako odwzorowanie XML DataSetu).

Do jednych łączy się jednocześnie pięciu userów, do innych - pieć tysięcy. Czasem jest jedno połączenie na dzień, czasem 200 na minutę.

Z tym wiąże się także problem blokad na rekordach. Jeśli będziesz miał stałe połączenie, możesz stosować mechanizmy bazodanowe (transakcyjne). Jeśli nie będziesz miał stałego połączenia, musisz robić własne locki (flagi + przeglądanie listy sesji), bo transakcyjne wygasną po zamknięciu połączenia (sesji).

Sam musisz zdecydować, czego potrzebujesz.

Jest tylko jeden ogólnik, "od zawsze" - oszczędnie z połączeniami. A co to znaczy "oszczędnie"? " Tylko tyle, ile rzeczywiście jest potrzebnych".Adrian Olszewski edytował(a) ten post dnia 31.07.09 o godzinie 14:23

konto usunięte

Temat: Połączenie z bazą

otwieranie połączeń do bazy danych jest dosyć kosztowne więc nie warto tego robić co chwila a jak już się połaczysz to pracuj na jednym mnożenie zbędnych bytów też jest kosztowne

konto usunięte

Temat: Połączenie z bazą

Przemysław R.:
otwieranie połączeń do bazy danych jest dosyć kosztowne więc nie warto tego robić co chwila a jak już się połaczysz to pracuj na jednym mnożenie zbędnych bytów też jest kosztowne

Dlatego wymyślono coś takiego jak pule połączeń. Po zamknięciu w kodzie połączenia nie jest ono faktycznie zamykane a tylko oddawane do puli i jeszcze przez jakiś czas pozostaje otwarte. Jeśli jakiś inny fragment aplikacji potrzebuje połączenia to jest ono mu z puli przydzielane. Wg. mnie to jest połączenie jednego i drugiego rozwiązania.

konto usunięte

Temat: Połączenie z bazą

Pula jest fajna, tyle że nie wszędzie

załóżmy mamy 100 klientów desktopowych każdy w puli ma 5 połączeń - co na to np. taki SQL SERVER?

za to w przypadku aplikacji w ASP.NET jest to rozwiązanie jak znalazł

Moim zdaniem metoda połączenia powinna być dobrana do konkretnego problemu, bez generalizowania
Norbert M.

Norbert M. Nobody's perfect.
Call me Nobody ;)

Temat: Połączenie z bazą

Ok. Dzięki wiem już wszystko co chciałem.
Maciej Filipiak

Maciej Filipiak właściciel, VizMedia

Temat: Połączenie z bazą

Po to mały miękki wymyślił DataSety w .NET i całą do nich otoczkę, żeby zamykać połączenia :)

Ba, nawet zamykać dostęp do sieci, nie przerywając pracy na bazie.
Norbert M.

Norbert M. Nobody's perfect.
Call me Nobody ;)

Temat: Połączenie z bazą

No ja też jestem zdania, że należy je zamykać i robię to praktycznie zawsze, ale prowadzę z kolegą dysputę na ten temat. On twierdzi, że nigdy nie zamykają połączeń ze względu na architekturę rozwiązań DevExpresso’wych. Wydało mi się to zastanawiające, dlatego poruszyłem ten temat.

Temat: Połączenie z bazą

Ponieważ prawdopodobnie taką mają architekturę :) Może coś monitoruje, może musi mieć szybki i ciągły dostęp do danych.

Sam pisałem oprogramowanie specjalistyczne, w którym połączenie było ciągle otwarte - ale przynajmniejich liczba była stała. Potem zmieniłem podejście i napisałem webserwis (WCF) po stronie klienta, który był wywoływany przez w triggerze na bazie (najpierw poprzez COM - sp_OACreate/sp_OAMethod, potem, jako CLR UDP). W efekcie miałem zgrabny monitoring danych (nie, nie zarzynałem serwera), a połączenie otwierało się tylko, gdy trigger "coś wyhaczył".

A potem moi następcy zastosowali Service Broker :)
Maciej Filipiak

Maciej Filipiak właściciel, VizMedia

Temat: Połączenie z bazą

oba podejścia są prawidłowe - i są bardziej kwestią wyboru, specyfikacji tego co robimy.
Nie są to zalecenia "należy zamykać"/"należy trzymać połączenie"

Mechanizm synchronizacji bazy przy pracy off-line jest ciekawie zrobiony w .NET i warto go stosować tam gdzie możemy pobrać paczkę danych, żeby na niej pracować - a potem pchnąć wyniki na serwer, jeszcze tak, żeby nie skopać żadnych constrainów i identity.

konto usunięte

Temat: Połączenie z bazą

Ja robię tak, że piszę kod, który _zawsze_ wywołuje SqlConnection.Open tak późno jak to możliwe i SqlConnection.Close tak wcześnie jak to możliwe.
Tak napisany kod w warstwie DAL może być potem użyty w różnych warunkach. I w zależności od tych warunków zawsze mogę odpowiednio skonfigurować Connection Poola - to się robi w connection stringu. Możliwe więc są scenariusze:
- pool wyłączony
- pool ustawiony na jedno połączenie, które nigdy nie wygasa
itd.

Reasumując: pisząc kod warto zamykać. Natomiast w connection stringu definiujemy jak ma się zachowywać _fizyczne_ połączenie.
Aby to sobie zobrazować warto odpalić sql profiler i się przekonać, że mimo wywołania SqlConnection.Close fizyczne połączenie wcale się nie musi zamykać.

Zaleta takiego podejścia to stworzenie jednego, dobrze przetestowanego kodu, natomiast decyzję o otwieraniu _fizycznego_ połączenia przerzucamy do pliku app.config a nie rekompilacji kodu.
Anna Cudzich

Anna Cudzich Software engineer,
Politechnika
Poznańska

Temat: Połączenie z bazą

Pozwolę sobie odświeżyć ten temat.
Czy stosowanie singletonów przy połaczeniach z bazą jest praktyką złą/niepolecaną? Chodzi o to, że mam klasę singleton, w której tworzę tablicę z połączeniami, następnie za pomocą metody GetConnection pobieram sobie połączenie. Jednak podczas load testów dostaję mnóstwo błedów z kodem 500 (closed stream error). Zmieniłam, żeby mi zwracało na sztywno jedno połączenie, problem jest taki sam. Dodam, że wywołuję open i close wtedy kiedy należy, a sama implementacja singleton'a jest "thread safe". Dopiero jak przeniosłam stworzenie i pobieranie połączeń poza singleton'a, to wszystko zaczęło śmigać.
Używam PostgreSQL'a i Npgsql'a do niego.
Może jest coś, o czym nie wiem?

konto usunięte

Temat: Połączenie z bazą

Anna Cudzich:
Pozwolę sobie odświeżyć ten temat.
Czy stosowanie singletonów przy połaczeniach z bazą jest praktyką złą/niepolecaną?

Ogólnie, moim prywatnym zdaniem, Singleton jest anti-patternem a nie desgin patternem. Jest to na tyle nadużywany wzorzec, że unikam go jak tylko mogę.

Jest *bardzo* mało sytuacji gdzie użycie Singletona jest jako-tako uzasadnione a i tak można to zastąpić sprytniejszym i bardziej logicznym schematem dostępu do danego obiektu.

Uważam, że zamiast trzymać jakiś obiekt w jednym miejscu w postaci Singletona, lepiej jest go podawać dalej w work-flow aplikacji. Program staję się prostszy do debugowania, ma logiczniejsze work-flow działania i mamy większą kontrolę nad życiem obiektu.
Chodzi o to, że mam klasę singleton, w której tworzę tablicę z połączeniami, następnie za pomocą metody GetConnection pobieram sobie połączenie.

To jest wynajdywanie koła na nowo. Takim działaniem robisz dokładnie to co robi mechanizm Connection Pooling w .NET z tą różnicą, że Connection Pooling działa w sposób bardziej przemyślany i uwzględniający więcej przypadków i sytuacji.

Już lepiej jakbyś za każdym razem otwierała nowe połączenie (SqlConnection, ISession, etc..) i pozwoliła puli na zarządzanie tymi połączeniami.
Jednak podczas load testów dostaję mnóstwo błedów z kodem 500 (closed stream error). Zmieniłam, żeby mi zwracało na sztywno jedno połączenie, problem jest taki sam. Dodam, że wywołuję open i close wtedy kiedy należy, a sama implementacja singleton'a jest "thread safe". Dopiero jak przeniosłam stworzenie i pobieranie połączeń poza singleton'a, to wszystko zaczęło śmigać.
Używam PostgreSQL'a i Npgsql'a do niego.
Może jest coś, o czym nie wiem?
Anna Cudzich

Anna Cudzich Software engineer,
Politechnika
Poznańska

Temat: Połączenie z bazą

Ok, czyli rozumiem, że jeśli stworzę nowe połączenie dla każdego nowego request'a:

NpgsqlConnection con = new NpgsqlConnection(ConnectionString);

przy czym mam ograniczenie MaxPoolSize np. do 50 i przypuśćmy, że mam 500 requestów/minute to .Net stworzy maksymalnie 50 połączeń i tyle będzie przechowywał i nimi zarządzał? Bo nie chciałabym przekroczyć tej maksymalnej liczby.

konto usunięte

Temat: Połączenie z bazą

Anna Cudzich:
Ok, czyli rozumiem, że jeśli stworzę nowe połączenie dla każdego nowego request'a:

NpgsqlConnection con = new NpgsqlConnection(ConnectionString);

przy czym mam ograniczenie MaxPoolSize np. do 50 i przypuśćmy, że mam 500 requestów/minute to .Net stworzy maksymalnie 50 połączeń i tyle będzie przechowywał i nimi zarządzał? Bo nie chciałabym przekroczyć tej maksymalnej liczby.

W dużym uproszczeniu to tak. Zgadza się.
Anna Cudzich

Anna Cudzich Software engineer,
Politechnika
Poznańska

Temat: Połączenie z bazą

Karim Agha:
Anna Cudzich:
Ok, czyli rozumiem, że jeśli stworzę nowe połączenie dla każdego nowego request'a:

NpgsqlConnection con = new NpgsqlConnection(ConnectionString);

przy czym mam ograniczenie MaxPoolSize np. do 50 i przypuśćmy, że mam 500 requestów/minute to .Net stworzy maksymalnie 50 połączeń i tyle będzie przechowywał i nimi zarządzał? Bo nie chciałabym przekroczyć tej maksymalnej liczby.

W dużym uproszczeniu to tak. Zgadza się.

Ok, to już wszystko wiem :P dzięki wielkie :)

konto usunięte

Temat: Połączenie z bazą

Karim Agha:

Ogólnie, moim prywatnym zdaniem, Singleton jest anti-patternem a nie desgin patternem. Jest to na tyle nadużywany wzorzec, że unikam go jak tylko mogę.

Karim mógłbyś rozwinąć temat? Dlaczego jesteś przeciwny temu wzorcowi? Nadużywanie wzorca (złe zastosowanie?) nie tłumaczy dlaczego jest on bee :)

konto usunięte

Temat: Połączenie z bazą

Jarek D.:
Karim Agha:

Ogólnie, moim prywatnym zdaniem, Singleton jest anti-patternem a nie desgin patternem. Jest to na tyle nadużywany wzorzec, że unikam go jak tylko mogę.

Karim mógłbyś rozwinąć temat? Dlaczego jesteś przeciwny temu wzorcowi? Nadużywanie wzorca (złe zastosowanie?) nie tłumaczy dlaczego jest on bee :)

Na wstępie chciałbym podkreślić, że to jest tylko moje subiektywne zdanie i styl projektowania.

Ogólnie rzecz biorąc, staram się projektując jakiś system nie doprowadzić do momentu kiedy elementy z różnych obszarów aplikacji w dziki sposób uzyskują dostęp do pewnego obiektu, gdzie nie mam kontroli nad tym w jakim stanie jest ten obiekt, nie mam kontroli nad przepływem tego obiektu.

Zazwyczaj sytuację w których używany jest singleton są takie, kiedy potrzebna nam jest tylko jedna instancja danego obiektu (np. centralnego rejestru, globalnej konfiguracji, czy współdzielone zasoby). Można taką sytuację rozwiązać prze wrzucenie obiektu z zasobem/konfiguracja/rejestrem w opakowanie Singletonu, w takim przypadku nie wiemy jaki obiekty mają do niego dostęp (często mechanizmy hermetyzacji klas nie są wystarczające) i wtedy jak się nam wkradnie do systemu cudzy kod (np. jakieś rozszerzenie, inny programista piszę po nas i nie do końca połapał się jeszcze w systemie albo używa go źle) może mieć dostęp do takiego Singletonu wtedy kiedy nie powinien mieć. Bo jak precyzyjnie skontrolujesz dostęp do instancji? (podpowiem, że "is" nie jest najlepszym pomysłem).

Można to rozwiązać w bardziej elegancki sposób poprzez przekazywanie instancji (referencji na instancję) tego obiektu dalej w workflow systemu jako parametry kolejnych wywołań konstruktorów, metod, itd...

W taki sposób masz pełną kontrolę nad przepływem obiektu i dostępem do niego. Co więcej, możesz w takiej sytuacji mieć większe możliwości optymalizacji z tego względu, że wiesz w danym momencie jakie obiektu będą miały dostęp do obiektu.

Po prostu odwracana jest kolejność: To nie aplikacja prosi i czyta mój obiekt tylko ja daję ten obiekt aplikacji.

Kolejnym powodem jest łatwość debugowania. W przypadku kiedy masz Singletona, czasem jest tak, że trudno stwierdzić jakie obiekty w jakiej kolejności wykonywały operacje na obiekcie singletona czy jakie zmienne zmieniały aż doprowadziły go do stanu niepoprawnego działania. W przypadku obiektu kontekstowego jest jasna ścieżka i kolejność dostępu do obiektu.

Powyższy argument (debugowanie) tyczy się obsługi konkurencji.

Tylko tak jak pisałem na początku: to jest tylko moje zdanie i (nie czytałem jeszcze) nie jest poparte jakimiś opiniami autorytetów z dziedziny OOP.

Pozdrawiam.

konto usunięte

Temat: Połączenie z bazą

Wzorce projektowe nie kolidują z zasadami OOP. Właśnie w oparciu o nie zostały zbudowane. Wstrzykiwanie zależności (przekazywanie jako argument w konstruktorze, wywołaniu metody) można jak najbardziej wykorzystać z singletonem.


internal interface IFoo
{
void Do();
}

internal sealed class Singleton : IFoo
{
static readonly IFoo instance = new Singleton();

static Singleton()
{
}

Singleton()
{
}

internal static IFoo Instance
{
get
{
return instance;
}
}

public void Do()
{
// do something
}
}

public interface IBar
{
}

public static class BarFactory
{
public static IBar Create()
{
return new Bar(Singleton.Instance);
}
}

internal class Bar : IBar
{
public Bar(IFoo foo)
{
foo.Do();
}
}
Jarek D. edytował(a) ten post dnia 29.01.10 o godzinie 09:08

konto usunięte

Temat: Połączenie z bazą

Chyba się nie zrozumieliśmy.
Wcale nie uważam, że Singeton koliduję z zasadami OOP (nie napisałem nigdzie tak).

Chodzi mi o to, żeby we wspomnianym przykładzie, przy tworzeniu obiektu Boo, to instancje IFoo nie była brana z singletonu a tworzona na początku działania programu i przekazywane dalej (między innymi do Bar).

To o czym mówię nie jest też wstrzykiwaniem zależności a "Context Object".

Tutaj jest kilka argumentów za i przeciw takiemu podejściu: http://misko.hevery.com/2008/10/27/pass-around-ginormo... .



Wyślij zaproszenie do