sql >> Databáze >  >> RDS >> Sqlserver

ExecuteReader vyžaduje otevřené a dostupné připojení. Aktuální stav připojení je Připojování

Omlouvám se za první komentář, ale téměř každý den posílám podobný komentář, protože mnoho lidí si myslí, že by bylo chytré zapouzdřit funkcionalitu ADO.NET do třídy DB (já také před 10 lety). Většinou se rozhodnou použít statické/sdílené objekty, protože se to zdá být rychlejší než vytvořit nový objekt pro jakoukoli akci.

To není dobrý nápad ani z hlediska výkonu, ani z hlediska bezpečnosti při selhání.

Nepytli na území fondu připojení

Existuje dobrý důvod, proč ADO.NET interně spravuje základní připojení k DBMS v ADO-NET Connection-Pool:

V praxi většina aplikací používá pouze jednu nebo několik různých konfigurací pro připojení. To znamená, že během spouštění aplikace se opakovaně otevírá a zavírá mnoho stejných spojení. Aby se minimalizovaly náklady na otevírání připojení, používá ADO.NET techniku ​​optimalizace zvanou sdružování připojení.

Sdružování připojení snižuje počet, kolikrát je nutné otevřít nová připojení. Sdružení udržuje vlastnictví fyzického připojení. Spravuje připojení udržováním aktivní sady aktivních připojení pro každou danou konfiguraci připojení. Kdykoli uživatel zavolá na připojení Otevřít, poskytovatel fondu hledá dostupné připojení ve fondu. Pokud je k dispozici sdružené připojení, vrátí jej volajícímu namísto otevření nového připojení. Když aplikace u připojení zavolá Zavřít, sdružovací služba jej vrátí do sdružené sady aktivních připojení namísto jeho uzavření. Jakmile se připojení vrátí do fondu, je připraveno k opětovnému použití v dalším otevřeném hovoru.

Zjevně tedy není důvod vyhýbat se vytváření, otevírání nebo zavírání spojení, protože ve skutečnosti se nevytvářejí, neotevírají a neuzavírají vůbec. Toto je „pouze“ příznak pro fond připojení, aby věděl, kdy lze připojení znovu použít nebo ne. Je to však velmi důležitý příznak, protože pokud je připojení „používáno“ (předpokládá fond připojení), nové fyzické připojení musí být otevřené pro DBMS, což je velmi drahé.

Nedosáhnete tedy žádného zlepšení výkonu, ale naopak. Pokud je dosaženo zadané maximální velikosti fondu (100 je výchozí hodnota), můžete dokonce získat výjimky (příliš mnoho otevřených připojení ...). Takže to bude mít obrovský dopad nejen na výkon, ale bude to také zdrojem nepříjemných chyb a (bez použití Transakcí) oblastí pro ukládání dat.

Pokud dokonce používáte statická připojení, vytváříte zámek pro každé vlákno, které se pokouší o přístup k tomuto objektu. ASP.NET je od přírody vícevláknové prostředí. Existuje tedy velká šance pro tyto zámky, které v nejlepším případě způsobují problémy s výkonem. Ve skutečnosti dříve nebo později získáte mnoho různých výjimek (např. váš ExecuteReader vyžaduje otevřené a dostupné připojení ).

Závěr :

  • Nepoužívejte opakovaně připojení ani žádné objekty ADO.NET.
  • Nedělejte z nich statické/sdílené (ve VB.NET)
  • Vždy je vytvářejte, otevírejte (v případě připojení), používejte, zavírejte a likvidujte je tam, kde je potřebujete (např. v metodě)
  • použijte using-statement zlikvidovat a uzavřít (v případě Připojení) implicitně

To platí nejen pro Connections (ačkoli nejpozoruhodnější). Každý objekt implementující IDisposable by měl být zlikvidován (nejjednodušeji pomocí using-statement ), tím více v System.Data.SqlClient jmenný prostor.

Vše výše uvedené hovoří proti vlastní třídě DB, která zapouzdřuje a znovu používá všechny objekty. To je důvod, proč jsem to komentoval, abych to vyhodil. To je pouze zdroj problému.

Upravit :Zde je možná implementace vaší 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. Oprava Msg 8116 „Datový typ argumentu varchar je neplatný pro argument 1 funkce session_context“ v SQL Server

  2. Vzory šablon a modifikátory pro formátování data/času v PostgreSQL

  3. Co vlastně znamená Clustered and Non-Clustered index?

  4. Nelze se připojit k databázi heroku postgresql z aplikace místního uzlu se sequelize