Introduction
I’m writing a web application (C#/ASP.NET MVC 3, .NET Framework 4, MS SQL Server 2008, System.Data.ODBC for database connections) and I’m having quite some issues regarding database creation/deletion.
I have a requirement that application should be able to create and delete databases.
Problem
Application fails stress testing for that function. More specifically, if client starts to quickly create, delete, create again a database with the same name then eventually (~on 5th request) server code throws ODBCException ‘Connection has been disabled.’. This behavior is observed on all machines that test has been performed on – the exact failing request may be not 5th but somewhere around that value.
Research
Googling on exception gave very low output – the exception seems very generic one and no analogue issues found. One of suggestions I’ve found was that my development Windows 7 might not be able to handle numerous simultaneous connections as it’s not Server OS. I’ve tried installing our app on Windows 2008 Server – almost no change in behavior, just a bit more requests processed before exception occurs.
Code and additional comments on implementation
Databases are created using stored procedure like this:
CREATE PROCEDURE [dbo].[sp_DBCreate]
...
@databasename nvarchar(124) -- 124 is max length of database file names
AS
DECLARE @sql nvarchar(150);
BEGIN
...
-- Create a new database
SET @sql = N'CREATE DATABASE ' + quotename(@databasename, '[');
EXEC(@sql);
IF @@ERROR <> 0
RETURN -2;
...
RETURN 0;
END
Databases are deleted using the following SP:
CREATE PROCEDURE [dbo].[sp_DomainDelete]
...
@databasename nvarchar(124) -- 124 is max length of database file names
AS
DECLARE @sql nvarchar(200);
BEGIN
...
-- check if database exists
IF EXISTS(SELECT * FROM [sys].[databases] WHERE [name] = @databasename)
BEGIN
-- drop all active connections
SET @sql = N'ALTER DATABASE' + quotename(@databasename, '[') + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE';
EXEC(@sql);
-- Delete database
SET @sql = N'DROP DATABASE ' + quotename(@databasename, '[');
EXEC(@sql);
IF @@ERROR <> 0
RETURN -1; --error deleting database
END
--ELSE database does not exist. consider it deleted.
RETURN 0;
END
In both SPs I’ve skipped less relevant parts like sanity checks.
I’m not using any ORMs, all SPs are called from code by using OdbcCommand instances. New OdbcConnection is created for each function call.
I sincerely hope someone might give me clue to the problem.
UPD: The exactly same problem occurs if we just rapidly create a bunch of databases. Thanks to everyone for suggestions on database delete code, but I’d prefer to have a solution or at least a hint for more general problem – the one which occurs even without deleting DBs at all.
UPD2: The following code is used for SP calls:
public static int ExecuteNonQuery(string sql, params object[] parameters)
{
try
{
var command = new OdbcCommand();
Prepare(command, new OdbcConnection( GetConnectionString() /*irrelevant*/), null, CommandType.Text, sql,
parameters == null ?
new List<OdbcParameter>().ToArray() :
parameters.Select(p => p is OdbcParameter ? (OdbcParameter)p : new OdbcParameter(string.Empty, p)).ToArray());
return command.ExecuteNonQuery();
}
catch (OdbcException ex)
{
// Logging here
throw;
}
}
public static void Prepare(
OdbcCommand command,
OdbcConnection connection,
OdbcTransaction transaction,
CommandType commandType,
string commandText,
params OdbcParameter[] commandParameters)
{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
command.Connection = connection;
command.CommandText = commandText;
if (transaction != null)
{
command.Transaction = transaction;
}
command.CommandType = commandType;
if (commandParameters != null)
{
command.Parameters.AddRange(
commandParameters.Select(p => p.Value==null &&
p.Direction == ParameterDirection.Input ?
new OdbcParameter(p.ParameterName, DBNull.Value) : p).ToArray());
}
}
Sample connection string:
Driver={SQL Server}; Server=LOCALHOST;Uid=sa;Pwd=<password here>;
Okay. There may be issues of scope for OdbcConnection but also you don’t appear to be closing connections after you’ve finished with them. This may mean that you’re reliant on the pool manager to close off unused connections and return them to the pool as they timeout. The
usingblock will automatically close and dispose of the connection when finished, allowing it to be returned to the connection pool.Try this code: