Ja, anslutningarna stängs aldrig. I stället kan du använda 'using':
using (OdbcConnection connection = new OdbcConnection(strConn))
{
// resten av koden
}
Då sköts stängning och uppstädning automagiskt.
20 svar · 861 visningar · startad av k0ffe
Hej!
Scenariot lyder:
Jag har ett projekt där jag läser in olika delar av sajten med funktionsanrop till funktioner, vilka jag har i en global class-fil under App_Code.
Nu har jag stött på problem under dessa experiment. Som rubriken säger får jag "Too many connections.."-exeptions ibland.
Kodexempel, default.aspx:
<h3 ID="h3_Title" runat="server" ></h3>
default.aspx.cs:
int pageID = 10;
h3_Title.InnerHtml = CommonFunctions.getPageTitle(pageID);
functions.cs
public static string strConn = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
public static string getPageTitle(int ID)
{
OdbcConnection connection = new OdbcConnection(strConn);
OdbcCommand cmd = new OdbcCommand();
OdbcDataReader dr;
cmd.Connection = connection;
cmd.CommandText = "SELECT title FROM page WHERE id = ?";
cmd.Parameters.AddWithValue("ID", ID);
connection.Open();
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (dr.Read())
{
return (string)dr["title"];
}
else
{
return null;
}
}
Kan man göra såhär? Rent generellt så känns det som att min connection aldrig stängs här:
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
Problemet inträffar alltså när jag tillämpar denna metod flertalet gånger på samma aspx-sida.
Bör jag istället skapa mitt connection-objekt i default.aspx.cs och skicka med detta i funktionsanropet för att, när jag har anropat alla funktioner, stänga det?
Ja, anslutningarna stängs aldrig. I stället kan du använda 'using':
using (OdbcConnection connection = new OdbcConnection(strConn))
{
// resten av koden
}
Då sköts stängning och uppstädning automagiskt.
Oj, snabbt svar!
OK, jag bör alltså bygga om funktionerna enligt nedan:
public static string getPageTitle(int ID)
{
using(OdbcConnection connection = new OdbcConnection(strConn))
{
OdbcCommand cmd = new OdbcCommand();
OdbcDataReader dr;
cmd.Connection = connection;
cmd.CommandText = "SELECT title FROM page WHERE id = ?";
cmd.Parameters.AddWithValue("ID", ID);
connection.Open();
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (dr.Read())
{
return (string)dr["title"];
}
else
{
return null;
}
}
}
Du kan t.o.m. dra det ett par steg längre:
public static string getPageTitle(int ID)
{
using(OdbcConnection connection = new OdbcConnection(strConn))
{
using (OdbcCommand cmd = new OdbcCommand())
{
cmd.Connection = connection;
cmd.CommandText = "SELECT title FROM page WHERE id = ?";
cmd.Parameters.AddWithValue("ID", ID);
connection.Open();
using (OdbcDataReader dr = cmd.ExecuteReader())
{
if (dr.Read())
{
return (string)dr["title"];
}
else
{
return null;
}
}
}
}
}
...Annars, i.o.m. att du säger 'CommandBehavior.CloseConnection' bör det räcka med att stänga 'dr' för att anslutningen skall stängas.
En följdfråga:
Varför ODBC ? Vad är det för databas du ansluter dig till, finns ingen .NET adapter ? ODBC är ganska slött jämfört med andra anslutningstekniker.
Peter:
...Annars, i.o.m. att du säger 'CommandBehavior.CloseConnection' bör det räcka med att stänga 'dr' för att anslutningen skall stängas.
Menar du då att mitt exempel borde fungera från start? Eller menar du att jag kan skippa den sista biten?
using (OdbcDataReader dr = cmd.ExecuteReader())
{
if (dr.Read())
{
return (string)dr["title"];
}
else
{
return null;
}
}
Vad gör egentligen "CommandBehavior.CloseConnection", och varför stängs anslutningen om jag använder "CommandBehavior.CloseConnection" OCH dr.Close() men inte annars? Anslutningen ska ju stängas iom
using(OdbcConnection connection = new OdbcConnection(strConn))
?
Zaiman:
DBn är mySQL, varför jag använder ODBC...
Har du inte möjlighet att installera .NET connectorn för MySQL eller har inte ditt webhotell någon?
Ditt exempel bör fungera från start OM du ser till att readern stängs. Detta gäller endast om också anger 'CommandBehavior.CloseConnection'. Använder du inte det sistnämnda måste du explicit stänga även anslutningen.
Jag föredrar att använda 'using'. Det direktivet ser till att objekt båda stängs OCH dispose:as.
Zaiman: Jag har en egen maskin för DB så jag KAN om jag vill, men varför vill jag göra det?
Peter: OK! Då kör jag på ditt förslag helt enkelt, vad spelar det för roll om det blir lite FÖR bra? :)
Tusen tack!
PeterS skrev:
Jag föredrar att använda 'using'. Det direktivet ser till att objekt båda stängs OCH dispose:as.
Framför allt så är man säker på att kopplingen stängs även om ett fel skulle inträffa mellan innanför ditt using-block
Om man inte använd using så skall man använda sig av Try-finally, eller Try-catch-finally. Och stänga sina kopplingar i finally blocket.
- M
Gladh: OK!
Jag har nu sett över mina connections mot DB:n och fick ner antalet connections / sidladdning från 4 till 1...
Gladh skrev:
Om man inte använd using så skall man använda sig av Try-finally, eller Try-catch-finally. Och stänga sina kopplingar i finally blocket.
- M
Måste man ha Finally?
Det går inte lika bra med
objConn.Open();
try
{
SqlDataAdapter da = new SqlDataAdapter(sql, objConn);
da.Fill(ds);
}
catch { }
objConn.Close();
?
Det dåliga med det sättet är att man sväljer alla exceptions, vilket innebär att alla databasfel brukar yttra sig på något supermystiskt sätt med NullReferenceException någonstans där det inte borde vara så. Bättre att använda finally och låta alla exceptions segla upp i anropshierarkin och ta hand om dem med ordentlig felhantering. Dessutom är det ett bättre sätt att säga "oavsett vad som händer i try/catch-en vill jag göra det här".
Sen är förstås using bättre än try/finally och jag ser egentligen aldrig några skäl att inte använda det om man har en resurs som bara används under ett anrop i en metod. Det är till och med bättre, har man flera resurser som man försöker disposa i finallyn kan en av dem kasta ett exception under disposandet och resultera i att de andra kvarvarande resurserna inte disposas.
k0ffe skrev:
Zaiman: Jag har en egen maskin för DB så jag KAN om jag vill, men varför vill jag göra det?
Ett ord prestanda :)
spango skrev:
Det dåliga med det sättet är att man sväljer alla exceptions, vilket innebär att alla databasfel brukar yttra sig på något supermystiskt sätt med NullReferenceException någonstans där det inte borde vara så. Bättre att använda finally och låta alla exceptions segla upp i anropshierarkin och ta hand om dem med ordentlig felhantering. Dessutom är det ett bättre sätt att säga "oavsett vad som händer i try/catch-en vill jag göra det här".
Sen är förstås using bättre än try/finally och jag ser egentligen aldrig några skäl att inte använda det om man har en resurs som bara används under ett anrop i en metod. Det är till och med bättre, har man flera resurser som man försöker disposa i finallyn kan en av dem kasta ett exception under disposandet och resultera i att de andra kvarvarande resurserna inte disposas.
Ok tack för en bra förklaring, jag ska anamma konceptet :bire
Peter S skrev:
Ja, anslutningarna stängs aldrig. I stället kan du använda 'using':
using (OdbcConnection connection = new OdbcConnection(strConn)) { // resten av koden }Då sköts stängning och uppstädning automagiskt.
Ska inte cmd.ExecuteReader(CommandBehavior.CloseConnection); göra att data readern stängs efter att man har läst klart? Varför finns den annars?
quickhelp skrev:
Ska inte
cmd.ExecuteReader(CommandBehavior.CloseConnection);leda till att stänga data readern efter att man läst klart?
Nej. Men om readern stängs så stängs även anslutningen.
Peter S skrev:
Nej. Men om readern stängs så stängs även anslutningen.
Och det är det som man slipper om man använder "using"?
Hur vet den att den ska anropa close() på readern då?
Kan tänka mig att allt som är deklarerat inuti "using" kanske garbage collectas, men hur vet den att den ska anropa close() ?
quickhelp skrev:
Och det är det som man slipper om man använder "using"?
Ja. Alternativet är ju att explicit kalla på Close().
quickhelp skrev:
Hur vet den att den ska anropa close() på readern då?
Using definierar ju ett scope, precis som andra statements. Så i slutet på det scopet anropas Dispose() automagiskt för de objekt som skapats. I det här fallet kan man väl anta att readerns Dispose() innehåller en funktion för att stänga.
Peter S skrev:
Ja. Alternativet är ju att explicit kalla på Close().
Using definierar ju ett scope, precis som andra statements. Så i slutet på det scopet anropas Dispose() automagiskt för de objekt som skapats. I det här fallet kan man väl anta att readerns Dispose() innehåller en funktion för att stänga.
Det där är ju riktigt användbart!
Ska börja använda det i min kod också :)
Tack för förklaringen.