butikoMedlem sedan jan. 2007152 inlägg Jag har lagt upp en test av en liten webb som jag jobbat med när jag haft en ledig stund över och det verkar som jag har fått problem med att db-kopplingarna inte avslutas.
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Jag har en db-klass som ärver från IDisposable och om jag förstått det rätt så ska en connection då stängas om man använder "using". Men det verkar den inte göra. Har satt max pool size till 200 och jag har ju svårt att tänka mig att jag skulle ha så mycket trafik och så många uppkopplingar att det ska bli problem...
Jag gör exempelvis så här för att returnera en datatable med data från databasen mha en sp:
public DataTable GetDataTable(string procName)
{
DataTable dt = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter(CreateCommand(procName, null)))
da.Fill(dt);
return dt;
}
Kör den inte både close() och dispose() då???
emissionMedlem sedan dec. 19996 721 inlägg I ditt exempel använder du using på en SqlDataAdapter-instans, vilket bara innebär att du kör Dispose på adaptern, vilket i sin tur bara gör att SelectCommand etc. dereferas. Om du vill ha någon verklig nytta av using så måste du köra det på din SqlConnection.
butikoMedlem sedan jan. 2007152 inlägg Hm, ok... så här ser min connection ut, vet du hur jag använder using på den?
private void Open()
{
// open connection
if (con == null)
{
con = new SqlConnection(ConfigurationManager.AppSettings["ConnStr"]);
con.Open();
}
}
butikoMedlem sedan jan. 2007152 inlägg Tillägg: Min funktion "CreateCommand" kör Open() och den är ju då indirekt omsluten av "using".
Först anropar jag exempelvis GetDataTable:
public DataTable GetDataTable(string procName)
Där skapar jag en sqldataadapter mha CreateCommand:
using (SqlDataAdapter da = new SqlDataAdapter(CreateCommand(procName, prams)))
Funktionen CreateCommand gör en connection och lägger till eventuella parametrar:
// Här gör jag en ny connection:
Open();
SqlCommand cmd = new SqlCommand(procName, con);
cmd.CommandType = CommandType.StoredProcedure;
if (prams != null)
{
foreach (SqlParameter parameter in prams)
cmd.Parameters.Add(parameter);
}
return cmd;
För att slutligen då fylla en datatable och returnera den.
da.Fill(dt);
return dt;
Allt detta sker ju innanför using?
butikoMedlem sedan jan. 2007152 inlägg Om jag explicit lägger till close och dispose innan jag returnerar min datatable bör det ju fungera men då har jag ju å andra sidan ingen nytta av att använda using? Finns det någon prestandafördel i att använda using framför att stänga ned kopplingen med close() och dispose()-metoderna?
public DataTable GetDataTable(string procName)
{
DataTable dt = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter(CreateCommand(procName, null)))
da.Fill(dt);
[blue][B] Close();
Dispose();[/B][/blue]
return dt;
}
emissionMedlem sedan dec. 19996 721 inlägg Det enda using gör är att anropa Dispose-metoden på det objekt som using-blocket initieras med.
Allt detta sker ju innanför using?
...vilket alltså inte spelar någon roll. Using är inget magiskt context/scope, utan bara en syntaktisk hjälp.
Finns det någon prestandafördel i att använda using framför att stänga ned kopplingen med close() och dispose()-metoderna?
Nej, för det är det enda som sker. Using anropar Dispose och (när det gäller SqlConnection) Dispose anropar Close. Du behöver mao aldrig anropa Close om du använder using eller Dispose.
Om du vill använda using så måste du göra det på själva SqlConnection-instansen, alternativt på instansen av din db-klass, där du i Dispose-metoden stänger uppkopplingen (eller Disposar) om den är öppen.
butikoMedlem sedan jan. 2007152 inlägg Ursäkta om jag är lite korkad (jag är inte programmerare egentligen) men då förstår jag inte hur jag rent syntaxmässigt ska använda using i mitt exempel eftersom jag har metoden open() som ju bara öppnar en connection... har jag using där så stänger jag ju ned min connection innan den används? Eller tänker jag helt fel?
Eller kan jag strunta i using och skriva så här istället:
private SqlConnection con;
public DataTable GetDataTable(string procName)
{
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(CreateCommand(procName, null));
da.Fill(dt);
con.Dispose();
return dt;
}
emissionMedlem sedan dec. 19996 721 inlägg Ett problem är att du skapar/öppnar din koppling i metoden CreateCommand. Dels är det ett opraktiskt ställe att göra det på, dels är det ologiskt att göra det i en metod som heter CreateCommand.
Om vi ska göra om så lite som möjligt så blir det bara en liten faktorering.
public DataTable GetDataTable(string procName)
{
using (SqlConnection con=new SqlConnection(ConnectionString))
{
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(CreateCommand(con,procName, null));
con.Open();
da.Fill(dt);
return dt;
}
}
eller
public class ButikosDbKlass : IDisposable
{
SqlConnection _con;
public ButikosDbKlass(string connectionString)
{
_con=new SqlConnection(connectionString);
}
private SqlConnection GetOpenConnection()
{
if (_con.State== ConnectionState.Closed)
_con.Open();
return _con;
}
public DataTable GetDataTable(string procName)
{
SqlConnection con=GetOpenConnection();
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(CreateCommand(con,procName, null));
da.Fill(dt);
return dt;
}
public void Dispose()
{
if (_con!=null)
_con.Dispose();
}
}
och
using (ButikosDbKlass b=new ButikosDbKlass("blablabla")
{
//blablabla
}
butikoMedlem sedan jan. 2007152 inlägg Toppen, då förstår jag. Tack så mycket för hjälpen! :bire
butikoMedlem sedan jan. 2007152 inlägg Hej igen! Blev inte riktigt klok på det här ändå tror jag... om jag gör ditt första alternativ så fungerar det visserligen, men bara om jag fortfarande öppnar en connection i min CreateCommand-funktion.
Alltså, först använder jag using och där kör jag min CreateCommand-funktion:
using (SqlConnection con = new SqlConnection(ConfigurationManager.AppSettings["ConnStr"]))
{
SqlDataAdapter da = new SqlDataAdapter(CreateCommand(procName, null));
I CreateCommand öppnade jag ju tidigare min connection mot databasen och det måste jag göra nu också, annars får jag fel:
private SqlCommand CreateCommand(string procName, SqlParameter[] prams)
{
[b]
[red] // här måste jag öppna en connection... igen!?
Open(); // öppnar en sqlconnection[/red]
[/b]
SqlCommand cmd = new SqlCommand(procName, con);
cmd.CommandType = CommandType.StoredProcedure;
[...]
Om jag inte öppnar en connection här så får jag felet:
System.InvalidOperationException: Fill: SelectCommand.Connection property has not been initialized.
Detta borde ju innebära att jag öppnar min connection två gånger, en gång i "using" och så en gång till i min CreateCommand-funktion... eller tänker jag fel?
butikoMedlem sedan jan. 2007152 inlägg Edit: Jo jag har fortfarande problem... får felet ovan ändå.
butikoMedlem sedan jan. 2007152 inlägg Jag löste det tror jag. Jag var tvungen att skicka med min sqlconnection in i CreateCommand, då fungerar det igen! :)
private SqlCommand CreateCommand(string procName, SqlParameter[] prams, [b][I][3]SqlConnection con[/3][/I][/b])
{
SqlCommand cmd = new SqlCommand(procName, [b]con[/b]);
emissionMedlem sedan dec. 19996 721 inlägg Japp, precis som det var i mitt exempel.. :)