sql >> Database teknologi >  >> RDS >> Sqlserver

ExecuteReader kræver en åben og tilgængelig forbindelse. Forbindelsens aktuelle tilstand er Forbinder

Undskyld for kun at kommentere i første omgang, men jeg poster næsten hver dag en lignende kommentar, da mange mennesker tror, ​​at det ville være smart at indkapsle ADO.NET-funktionalitet i en DB-klasse (også mig for 10 år siden). For det meste beslutter de sig for at bruge statiske/delte objekter, da det ser ud til at være hurtigere end at oprette et nyt objekt til enhver handling.

Det er hverken en god idé med hensyn til ydeevne eller med hensyn til fejlsikkerhed.

Lad være med at pochere på Connection-Pools område

Der er en god grund til, at ADO.NET internt administrerer de underliggende forbindelser til DBMS i ADO-NET Connection-Pool:

I praksis bruger de fleste applikationer kun én eller nogle få forskellige konfigurationer til forbindelser. Dette betyder, at mange identiske forbindelser vil blive åbnet og lukket gentagne gange under applikationsudførelse. For at minimere omkostningerne ved at åbne forbindelser bruger ADO.NET en optimeringsteknik kaldet forbindelsespooling.

Forbindelsespooling reducerer antallet af gange, nye forbindelser skal åbnes. Pooleren bevarer ejerskabet af den fysiske forbindelse. Den administrerer forbindelser ved at holde liv i et sæt aktive forbindelser for hver given forbindelseskonfiguration. Når en bruger ringer til Åbn på en forbindelse, leder pooleren efter en tilgængelig forbindelse i poolen. Hvis en samlet forbindelse er tilgængelig, returnerer den den til den, der ringer i stedet for at åbne en ny forbindelse. Når applikationen kalder Luk på forbindelsen, returnerer pooleren den til det samlede sæt af aktive forbindelser i stedet for at lukke den. Når forbindelsen er returneret til poolen, er den klar til at blive genbrugt ved næste åbent opkald.

Så åbenbart er der ingen grund til at undgå at oprette, åbne eller lukke forbindelser, da de faktisk slet ikke oprettes, åbnes og lukkes. Dette er "kun" et flag for forbindelsespuljen for at vide, hvornår en forbindelse kan genbruges eller ej. Men det er et meget vigtigt flag, for hvis en forbindelse er "i brug" (forbindelsespuljen forudsætter), skal en ny fysisk forbindelse være åben til DBMS, hvilket er meget dyrt.

Så du opnår ingen præstationsforbedring, men det modsatte. Hvis den angivne maksimale poolstørrelse (100 er standard) nås, vil du endda få undtagelser (for mange åbne forbindelser ...). Så dette vil ikke kun påvirke ydeevnen enormt, men også være en kilde til grimme fejl og (uden at bruge Transaktioner) et data-dumping-område.

Hvis du endda bruger statiske forbindelser, opretter du en lås for hver tråd, der forsøger at få adgang til dette objekt. ASP.NET er et multithreading-miljø af natur. Så der er en stor chance for disse låse, som i bedste fald forårsager ydeevneproblemer. Faktisk vil du før eller siden få mange forskellige undtagelser (såsom din ExecuteReader kræver en åben og tilgængelig forbindelse ).

Konklusion :

  • Genbrug ikke forbindelser eller nogen ADO.NET-objekter overhovedet.
  • Gør dem ikke statiske/delte (i VB.NET)
  • Opret, åbn (i tilfælde af forbindelser), brug, luk og bortskaf dem altid, hvor du har brug for dem (f.eks. i en metode)
  • brug using-statement at bortskaffe og lukke (i tilfælde af forbindelser) implicit

Det gælder ikke kun for forbindelser (selvom det er mest bemærkelsesværdigt). Hvert objekt, der implementerer IDisposable skal bortskaffes (simpelt ved using-statement ), så meget desto mere i System.Data.SqlClient navneområde.

Alt ovenstående taler imod en brugerdefineret DB-klasse, som indkapsler og genbruger alle objekter. Det er grunden til, at jeg kommenterede for at smide det. Det er kun en problemkilde.

Rediger :Her er en mulig implementering af din retrievePromotion -metode:

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. INST_TOP (Oracle R12 INSTANCE_HOME ) afkodet

  2. Er det muligt at oprette Oracle Database-objekttyper inde i PL/SQL?

  3. PostgreSQL brug værdi fra forrige række, hvis den mangler

  4. Sådan bruger du flere databaser dynamisk til en model i CakePHP