Сообщение об ошибке «Соединение было отключено» при быстром создании / удалении баз - PullRequest
2 голосов
/ 21 декабря 2011

Введение

Я пишу веб-приложение (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>;

1 Ответ

1 голос
/ 21 декабря 2011

Хорошо.Могут быть проблемы с областью действия для OdbcConnection, но вы также не закрываете соединения после того, как закончите с ними.Это может означать, что вы используете диспетчер пула, чтобы закрыть неиспользуемые соединения и вернуть их в пул по истечении времени ожидания.Блок using автоматически закроет и удалит соединение по завершении, что позволит вернуть его в пул соединений.

Попробуйте этот код:

public static int ExecuteNonQuery(string sql, params object[] parameters)
{
    int result = 0;
        try
        {
            var command = new OdbcCommand();
            using (OdbcConnection connection = new OdbcConnection(GetConnectionString() /*irrelevant*/))
            {
               connection.Open();
               Prepare(command, connection, null, CommandType.Text, sql,
                       parameters == null ?
                                           new List<OdbcParameter>().ToArray() :
                                           parameters.Select(p => p is OdbcParameter ? (OdbcParameter)p : new OdbcParameter(string.Empty, p)).ToArray());

               result = command.ExecuteNonQuery();
            }

        }
        catch (OdbcException ex)
        {
            // Logging here
            throw;
        }
    return result;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...