Использование или удаление SqlConnection не работает, но Close работает? - PullRequest
0 голосов
/ 19 сентября 2019

Я прочитал многочисленные посты о том, почему вы должны отдавать предпочтение оператору using, а не вручную .Open(), затем .Close() и, наконец, .Dispose().

Когда я изначально писал свой код, ябыло что-то вроде этого:

private static void doIt(string strConnectionString, string strUsername)
{
    SqlConnection conn = new SqlConnection(strConnectionString);
    try
    {
        conn.Open();
        string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
        SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
        var sqlNonReader = sqlCommand.ExecuteNonQuery();
        if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
    finally
    {
        conn.Close();
        conn.Dispose();
    }
}

, и это работает ... без проблем.но только ОДИН РАЗ.

, поэтому, если я сделаю что-то вроде этого:

private static void doItLots(string strConnectionString, string strUsername)
{
    for(int i=0; i<10; i++)
    {
        doIt(strConnectionString, $"{strUsername}_{i}");
    }
}

, он будет работать ПЕРВЫЙ раз, когда i=0, но любые последующие итерации завершатся неудачно с Cannot open database "myDbName" requested by the login. The login failed.

Однако, если я вернусь и закомментирую строку conn.Dispose();, то она прекрасно работает на всех итерациях.

Проблема заключается просто в том, что если я хочу выполнить часть .Dispose() внеметод, тогда я вынужден передать объект SqlConnection вместо простой передачи учетных данных, что потенциально делает мой код немного менее переносимым, а затем мне нужно также поддерживать соединение дольше.У меня всегда было впечатление, что вы хотите быстро открывать и закрывать соединения, но ясно, что я неправильно понимаю, как работает команда .Dispose().

Как я уже говорил, я также пытался сделать это с помощью using вот так ...

private static void doIt(string strConnectionString, string strUsername)
{
    using (SqlConnection conn = new SqlConnection(strConnectionString))
    {
        try
        {
            conn.Open();
            string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
            SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
            var sqlNonReader = sqlCommand.ExecuteNonQuery();
            if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
        finally
        {
            conn.Close();
        }
    }
}

, и это делает то же самое, что и исходный код с .Dispose(), вызываемым вручную.

Любая помощь здесь будет принята с благодарностью.Я бы хотел преобразовать в операторы using, но мне было бы трудно выяснить, как таким образом писать повторно используемые методы ...

ОБНОВЛЕНИЕ:

Я немного сузил его.Проблема НЕ в итерациях или повторных вызовах.Но я все еще получаю ошибку доступа.Вот код:

        string strConnectionString = $@"Data Source={StrSqlServerDataSource};Initial Catalog={StrDatabaseName};User id={StrSqlServerMasterUser};Password={StrSqlServerMasterPassword}";

        using (SqlConnection connUserDb = new SqlConnection(strConnectionString))
        {
            try
            {
                Utility.Notify($"Connection State: {connUserDb.State.ToString()}"); // Responds as 'Closed'
                connUserDb.Open(); // <-- throws error
                Utility.Notify($"Connection State: {connUserDb.State.ToString()}");

                Utility.Notify($"MSSQL Connection Open... Adding User '{strUsername}' to Database: '{strDatabaseName}'");

                string sqlCommandText =
                    //$@"USE {StrDatabaseName}; " +
                    $@"CREATE USER [{strUsername}] FOR LOGIN [{strUsername}] WITH DEFAULT_SCHEMA = [dbo]; " +
                    $@"ALTER ROLE [db_datareader] ADD MEMBER [{strUsername}]; " +
                    $@"ALTER ROLE [db_datawriter] ADD MEMBER [{strUsername}]; " +
                    $@"ALTER ROLE [db_ddladmin] ADD MEMBER [{strUsername}]; ";
                using (SqlCommand sqlCommand = new SqlCommand(sqlCommandText, connUserDb))
                {
                    var sqlNonReader = sqlCommand.ExecuteNonQuery();
                    if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername} ({sqlNonReader})");
                }

                result = true;
            }
            catch (Exception ex)
            {
                Utility.Notify($"Creating User and Updating Roles Failed: {ex.Message}", Priority.High);
            }
            finally
            {
                connUserDb.Close();
                Utility.Notify($"MSSQL Connection Closed");
            }
        }
        return result;
    }

Ошибка, которую я получаю здесь: Cannot open database requested by the login. The login failed.

Я имею в виду, что до этого я выполнял этот же код с двумя изменениями:

1) раскомментировал оператор USE в sqlCommandText 2), подключенный к базе данных Master вместо

Когда я это сделал, он тоже не работал, и вместо этогоЯ получил эту ошибку: The server principal is not able to access the database under the current security context.

Если я захожу в SSMS и просматриваю MasterUser, они перечислены как db_owner, и я могу выполнять любые действия, которые я хочу, включая выполнение команды, включенной в код выше.

Я переписал весь код, чтобы использовать одно соединение согласно рекомендациям здесь.После появления ошибки «принципала сервера» я добавил еще одно подключение, чтобы попытаться напрямую подключиться к этой базе данных, а не master.

ОБНОВЛЕНИЕ 2:

Вот еще один поворот сюжета...

Это нормально работает с моего локального компьютера (сейчас).Но (не всегда) работает при запуске из веб-задания Azure, предназначенного для сервера реляционных баз данных (RDS) Amazon Web Services (AWS), на котором выполняется MSSQL.

Мне придется проверять коммиты git завтра, но с5p сегодня, это работало на обоих местных и Azure.После последнего обновления я смог протестировать локальный и заставить его работать, но при запуске на веб-задании Azure произошел сбой, как описано выше.

1 Ответ

0 голосов
/ 19 сентября 2019

SqlConnection реализует IDisposable.Вы не звоните утилизировать или закрыть.

try{
 using (SqlConnection conn = new SqlConnection(strConnectionString))
    {

            conn.Open();
            string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
            SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
            var sqlNonReader = sqlCommand.ExecuteNonQuery();
            if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");

    }}
catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
...