Введение
Я пишу веб-приложение (C # / ASP.NET MVC 3, .NET Framework 4, MS SQL Server 2008, System.Data.ODBC для соединений с базой данных) и у меня довольно много проблем, связанных с созданием / удалением базы данных.
У меня есть требование, чтобы приложение могло создавать и удалять базы данных.
Проблема
Приложение не проходит стресс-тестирование для этой функции.В частности, если клиент начинает быстро создавать, удалять, снова создавать базу данных с тем же именем, то в конечном итоге (~ по 5-му запросу) серверный код выдает ODBCException «Соединение было отключено».Такое поведение наблюдается на всех машинах, на которых проводился тест - точный ошибочный запрос может быть не 5-м, а где-то около этого значения.
Research
Поиск в Google при исключениидал очень низкий выходной сигнал - исключение кажется очень общим и аналоговых проблем не найдено.Одно из предложений, которое я обнаружил, заключалось в том, что моя разработка Windows 7 может не справиться с многочисленными одновременными подключениями, поскольку это не серверная ОС.Я попытался установить наше приложение на Windows 2008 Server - практически без изменений в поведении, только немного больше запросов было обработано до возникновения исключения.
Код и дополнительные комментарии по реализации
Базы данных создаются с помощью хранимой процедуры, подобной этой:
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
Базы данных удаляются с использованием следующего 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
В обоих SP я пропустил менее важные части, такие как проверки работоспособности.
Я не использую ORM, все SP вызываются из кода с использованием OdbcCommand
экземпляров.Новый OdbcConnection
создается для каждого вызова функции.
Я искренне надеюсь, что кто-то может подсказать мне эту проблему.
UPD : точно такая же проблема возникает, если мыпросто быстро создать кучу баз данных.Спасибо всем за предложения по коду удаления базы данных, но я бы предпочел иметь решение или хотя бы подсказку для более общей проблемы - той, которая возникает даже без удаления БД.
UPD2: для вызовов SP используется следующий код:
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());
}
}
Пример строки подключения:
Driver={SQL Server}; Server=LOCALHOST;Uid=sa;Pwd=<password here>;