Podczas próby połączenia się z bazą danych MSSQL przez ASP.NET online, otrzymam następujące informacje, gdy dwie lub więcej osób połączą się jednocześnie:
ExecuteReader wymaga otwartego i dostępnego połączenia. Bieżący stan połączenia to Łączenie.
Strona działa dobrze na moim serwerze localhost.
To jest przybliżony kod.
public Promotion retrievePromotion()
{
int promotionID = 0;
string promotionTitle = "";
string promotionUrl = "";
Promotion promotion = null;
SqlOpenConnection();
SqlCommand sql = SqlCommandConnection();
sql.CommandText = "SELECT TOP 1 PromotionID, PromotionTitle, PromotionURL FROM Promotion";
SqlDataReader dr = sql.ExecuteReader();
while (dr.Read())
{
promotionID = DB2int(dr["PromotionID"]);
promotionTitle = DB2string(dr["PromotionTitle"]);
promotionUrl = DB2string(dr["PromotionURL"]);
promotion = new Promotion(promotionID, promotionTitle, promotionUrl);
}
dr.Dispose();
sql.Dispose();
CloseConnection();
return promotion;
}
Czy mogę wiedzieć, co mogło pójść nie tak i jak to naprawić?
Edycja: nie zapomnij, moje parametry połączenia i połączenie są statyczne. Myślę, że to jest powód. Proszę doradź.
public static string conString = ConfigurationManager.ConnectionStrings["dbConnection"].ConnectionString;
public static SqlConnection conn = null;
c#
.net
sql-server
ado.net
database-connection
Guo Hong Lim
źródło
źródło
conString
nie dodaje nic w zakresie wydajności, ponieważ i tak jest domyślnie buforowany (jak każda wartość konfiguracyjna dla bieżącej aplikacji).Odpowiedzi:
Przepraszam, że przede wszystkim komentuję, ale publikuję prawie codziennie podobny komentarz, ponieważ wiele osób uważa, że rozsądnie byłoby zawrzeć funkcjonalność ADO.NET w klasie DB (ja też 10 lat temu). Przeważnie decydują się na użycie obiektów statycznych / współdzielonych, ponieważ wydaje się to być szybsze niż tworzenie nowego obiektu dla dowolnej akcji.
Nie jest to ani dobry pomysł, ani z punktu widzenia wydajności, ani bezpieczeństwa.
Nie kłusuj na terytorium puli połączeń
Istnieje dobry powód, dla którego ADO.NET wewnętrznie zarządza podstawowymi połączeniami z systemem DBMS w puli połączeń ADO-NET :
Więc oczywiście nie ma powodu, aby unikać tworzenia, otwierania lub zamykania połączeń, ponieważ w rzeczywistości nie są one w ogóle tworzone, otwierane ani zamykane. To jest „tylko” flaga dla puli połączeń, aby wiedzieć, kiedy połączenie może zostać ponownie użyte, czy nie. Jest to jednak bardzo ważna flaga, ponieważ jeśli połączenie jest „w użyciu” (zakłada pula połączeń), nowe połączenie fizyczne musi zostać otwarte dla DBMS, co jest bardzo kosztowne.
Więc nie uzyskujesz poprawy wydajności, ale odwrotnie. Jeśli maksymalny określony rozmiar puli (domyślnie 100) zostanie osiągnięty, wystąpią nawet wyjątki (zbyt wiele otwartych połączeń ...). Więc to nie tylko wpłynie ogromnie na wydajność, ale także będzie źródłem nieprzyjemnych błędów i (bez korzystania z Transakcji) obszarem zrzucania danych.
Jeśli używasz nawet połączeń statycznych, tworzysz blokadę dla każdego wątku próbującego uzyskać dostęp do tego obiektu. ASP.NET jest z natury środowiskiem wielowątkowym. Istnieje więc duża szansa na te blokady, co w najlepszym przypadku powoduje problemy z wydajnością. Właściwie wcześniej czy później otrzymasz wiele różnych wyjątków (na przykład Twój ExecuteReader wymaga otwartego i dostępnego połączenia ).
Wniosek :
using-statement
aby usunąć i zamknąć (w przypadku połączeń) niejawnieDotyczy to nie tylko Connections (choć najbardziej zauważalne). Każdy obiekt implementujący
IDisposable
powinien zostać usunięty (najprościej przezusing-statement
), tym bardziej wSystem.Data.SqlClient
przestrzeni nazw.Wszystko to przemawia przeciwko niestandardowej klasie DB, która hermetyzuje i ponownie wykorzystuje wszystkie obiekty. To jest powód, dla którego skomentowałem, aby to wyrzucić. To tylko źródło problemu.
Edycja : Oto możliwa implementacja Twojej metody
retrievePromotion
:źródło
Kilka dni temu złapałem ten błąd.
W moim przypadku było tak, ponieważ korzystałem z transakcji na singletonie.
.Net nie działa dobrze z Singletonem, jak wspomniano powyżej.
Moje rozwiązanie było takie:
Użyłem HttpContext.Current.Items dla mojej instancji. Ta klasa DbHelper i DbHelperCore jest moją własną klasą
źródło