Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

ExecuteReader wymaga otwartego i dostępnego połączenia. Obecny stan połączenia to Łączenie

Przepraszam za komentowanie w pierwszej kolejności, ale prawie codziennie publikuję podobny komentarz, ponieważ wiele osób uważa, że ​​byłoby mądrze 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 szybsze niż tworzenie nowego obiektu dla jakiejkolwiek akcji.

To nie jest dobry pomysł pod względem wydajności ani pod względem 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 do DBMS w puli połączeń ADO-NET:

W praktyce większość aplikacji używa tylko jednej lub kilku różnych konfiguracji połączeń. Oznacza to, że podczas wykonywania aplikacji wiele identycznych połączeń będzie wielokrotnie otwieranych i zamykanych. Aby zminimalizować koszt otwierania połączeń, ADO.NET używa techniki optymalizacji zwanej pulą połączeń.

Pula połączeń zmniejsza liczbę razy, kiedy nowe połączenia muszą być otwierane. Pooler zachowuje własność fizycznego połączenia. Zarządza połączeniami, utrzymując przy życiu zestaw aktywnych połączeń dla każdej danej konfiguracji połączenia. Za każdym razem, gdy użytkownik wywołuje Open w połączeniu, pooler szuka dostępnego połączenia w puli. Jeśli połączenie w puli jest dostępne, zwraca je do wywołującego zamiast otwierania nowego połączenia. Gdy aplikacja wywołuje Close w połączeniu, program pooler zwraca je do puli połączeń aktywnych, zamiast je zamykać. Gdy połączenie zostanie przywrócone do puli, jest gotowe do ponownego użycia w następnym wywołaniu Open.

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 i zamykane. Jest to "tylko" flaga dla puli połączeń, aby wiedzieć, kiedy połączenie może być ponownie użyte, czy nie. Ale jest to bardzo ważna flaga, ponieważ jeśli połączenie jest "używane" (zakłada pula połączeń), nowe połączenie fizyczne musi być otwarte dla DBMS, co jest bardzo kosztowne.

Więc nie zyskujesz poprawy wydajności, ale odwrotnie. Jeśli zostanie osiągnięty maksymalny określony rozmiar puli (100 jest wartością domyślną), otrzymasz nawet wyjątki (zbyt wiele otwartych połączeń...). Więc to nie tylko ogromnie wpłynie na wydajność, ale także będzie źródłem nieprzyjemnych błędów i (bez korzystania z Transakcji) obszarem zrzutu 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. Jest więc duża szansa na te blokady, które w najlepszym przypadku powodują problemy z wydajnością. Właściwie prędzej czy później pojawi się wiele różnych wyjątków (np. Twój ExecuteReader wymaga otwartego i dostępnego połączenia ).

Wniosek :

  • W ogóle nie używaj ponownie połączeń ani żadnych obiektów ADO.NET.
  • Nie rób ich statycznych/współdzielonych (w VB.NET)
  • Zawsze twórz, otwieraj (w przypadku połączeń), używaj, zamykaj i usuwaj je tam, gdzie ich potrzebujesz (np. w metodzie)
  • użyj instrukcji using-statement usunąć i zamknąć (w przypadku połączeń) niejawnie

Dotyczy to nie tylko Connections (choć najbardziej zauważalne). Każdy obiekt implementujący IDisposable powinien zostać usunięty (najprostszy przez using-statement ), tym bardziej w System.Data.SqlClient przestrzeń nazw.

Wszystko powyższe przemawia przeciwko niestandardowej klasie DB, która hermetyzuje i ponownie wykorzystuje wszystkie obiekty. To jest powód, dla którego skomentowałem to do kosza. To tylko źródło problemów.

Edytuj :Oto możliwa implementacja Twojej retrievePromotion -metoda:

public Promotion retrievePromotion(int promotionID)
{
    Promotion promo = null;
    var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE [email protected]";
        using (var da = new SqlDataAdapter(queryString, connection))
        {
            // you could also use a SqlDataReader instead
            // note that a DataTable does not need to be disposed since it does not implement IDisposable
            var tblPromotion = new DataTable();
            // avoid SQL-Injection
            da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
            da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
            try
            {
                connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise 
                da.Fill(tblPromotion);
                if (tblPromotion.Rows.Count != 0)
                {
                    var promoRow = tblPromotion.Rows[0];
                    promo = new Promotion()
                    {
                        promotionID    = promotionID,
                        promotionTitle = promoRow.Field<String>("PromotionTitle"),
                        promotionUrl   = promoRow.Field<String>("PromotionURL")
                    };
                }
            }
            catch (Exception ex)
            {
                // log this exception or throw it up the StackTrace
                // we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
                throw;
            }
        }
    }
    return promo;
}


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tworzenie indeksu ciągów z kodem jako pierwszy

  2. Co to jest typ danych SYSNAME w SQL Server?

  3. Jak usunąć z bazy danych datę i godzinę, która może mieć wartość null

  4. OPENROWSET nie akceptuje zmiennych dla swoich argumentów (SQL Server)

  5. Jak monitorować zmiany w tabeli programu SQL Server za pomocą c#?