i Dispose an SqlConnection object, but of of course it isn't really closed. i need closed connection to not hold locks on database objects. How can i prevent closed connections from holding locks?
Explanation of the above sentance for those who don't know:
When you close an ADO or ADO.NET connection, you aren't actually severing the connection to SQL Server. The ADO/ADO.NET infrastructure keeps the connection around in case you want to use it again. The connections are kept lingering around in what's called "The Connection Pool".
After a few minutes of not being used, the connection will be actually closed. Although, not really. TCP/IP has it's own method for keeping TCP connections open for a few more minutes (in the "CLOSE_WAIT" state). This is done in case you ask to open a TCP connection to the same IP:Port again. If so, it can use that already open TCP connection.
With connection pooling and SQL Server, the connection is still established to SQL Server. Each connection has a database context that it is sitting in. As long as a connection is sitting in that database: it holds a shared database (S-DB) lock on that database.
A Shared-Database lock simply means, "Don't delete this database while i'm in it please."
How can i prevent it from holding a shared lock on my database, while keeping the benefits of connection pooling?
My ad-hoc solution right now is every time a developer called Dispose:
connection.Dispose()
change it into a call to a global helper function:
Database.DisposeConnection(connection);
which changes the database context to master:
public static void DisposeConnection(SqlConnection connection)
{
//Stop holding a database lock - in my database at least
ADOHelper.ExecuteNonQuery(connection, "USE master");
connection.Dispose();
}
It solves my immediate problem; closed connections aren't holding a lock on my database.
But now i'm worried that connection pooling will have its brain scrambled - because i switched database contexts behind its back.
In case anyone didn't know, or thought otherwise:
From the SDK:
Close and Dispose are functionally equivalent.
You can place the database in single user mode in order to restore it. IIRC, something like this...
ALTER DATABASE TheDatabaseToRestore SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE TheDatabaseToRestore
FROM DISK =N'Z:\Path\To\Backup\BackupFile';
ALTER DATABASE TheDatabaseToRestore SET MULTI_USER;
see: Obtain Exclusive Access to Restore SQL Server and/or Restore Database Backup using SQL for some more background.
Edit: Single user mode is intended for backups and restores (which, on the non-express edition, is what I use it for regularly). Using it kicks out all other connections, and new connections cannot be made. I haven't played with the various "WITH" options such as with "ROLLBACK IMMEDIATE", but their usage seems straightforward.
Have you tried SqlClient.ClearPool?
From MSDN:
ClearPool clears the connection pool that is associated with the connection. If additional connections associated with connection are in use at the time of the call, they are marked appropriately and are discarded (instead of being returned to the pool) when Close is called on them.
Just call ClearPool on every connection, but you lose the pooling benefits if you do this.
public class DataFactory
{
public SqlConnection CreateConnection(string connString)
{
SqlConnection conn = new SqlConnection(connString);
SqlClient.ClearPool(conn);
return conn;
}
}
Alternatively, you can disable pooling all together using the Pooling property of the connection string:
string connString = "Data Source=MYSERVER;Initial Catalog=myDb;Integrated Security=true;Pooling=false"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With