Обнаружение непригодных SqlConnections в пуле

Когда я пытаюсь установить роль приложения в SqlConnection с помощью sp_setappprole, я иногда появляется следующая ошибка в журнале событий Windows...

Соединение было разорвано, поскольку открывший его участник впоследствии принял новый контекст безопасности, а затем попытался сбросить соединение в олицетворенном контексте безопасности. Этот сценарий не поддерживается. См. «Обзор олицетворения» в электронной документации.)

... и в моем приложении возникает соответствующее исключение.

Это пулы соединений, и было время, когда пулы соединений были несовместимы с ролями приложений — на самом деле старый совет Microsoft заключался в том, чтобы отключить пул соединений (!!), но с введением sp_unsetapprole теперь (теоретически) можно очистить соединение перед его возвратом в пул.

Я считаю, что эти ошибки возникают, когда (по неизвестным причинам) sp_unsetapprole не запускается для соединения до его закрытия и возврата в пул соединений. Затем sp_approle обречен на сбой, когда это соединение возвращается из пула.

Я могу поймать и обработать это исключение, но я бы предпочел обнаружить надвигающийся сбой и вообще избежать исключения (и сообщений в журнале событий).

Можно ли обнаружить проблему, не вызывая исключения?

Мысли или советы приветствуются.


person Ed Guiness    schedule 16.02.2009    source источник


Ответы (4)


Это происходит по логике и не требует большого опыта использования sp_setapprole, но нельзя ли проверить контекст безопасности перед выполнением вызова? Или, в качестве альтернативы, сначала проверьте разрешение и контекст безопасности?

person BinaryMisfit    schedule 16.02.2009
comment
Насколько я знаю, невозможно выполнить ЛЮБОЙ sql на непригодном для использования соединении без исключения. Поэтому я не могу опросить соединение, чтобы проверить контекст безопасности. - person Ed Guiness; 17.02.2009
comment
Справедливо. Тогда отбросьте эту идею. Однако вы не можете проверить другое соединение или это влияет на все приложение? - person BinaryMisfit; 17.02.2009
comment
Это пул соединений, поэтому, хотя я могу получить другое соединение, я не могу запросить с ним состояние первого соединения. Все, что у меня есть, это соединение, которое может быть использовано, а может и нет, я не знаю, пока не попытаюсь его использовать. - person Ed Guiness; 17.02.2009

Казалось бы, вы вызываете sp_setapprole, но не вызываете sp_unsetapprole, а затем позволяете просто вернуть соединение в пул.

Я бы предложил использовать структуру (или класс, если вам нужно использовать это в методах) с реализацией IDisposable, которая позаботится об этом за вас:

public struct ConnectionManager : IDisposable
{
    // The backing for the connection.
    private SqlConnection connection;

    // The connection.
    public SqlConnection Connection { get { return connection; } }

    public void Dispose()
    {
        // If there is no connection, get out.
        if (connection == null)
        {
            // Get out.
            return;
        }

        // Make sure connection is cleaned up.
        using (SqlConnection c = connection)
        {
            // See (1).  Create the command for sp_unsetapprole
            // and then execute.
            using (SqlCommand command = ...)
            {
                // Execute the command.
                command.ExecuteNonQuery();
            }
        }
    }

    public ConnectionManager Release()
    {
        // Create a copy to return.
        ConnectionManager retVal = this;

        // Set the connection to null.
        retVal.connection = null;

        // Return the copy.
        return retVal;        
    }

    public static ConnectionManager Create()
    {
        // Create the return value, use a using statement.
        using (ConnectionManager cm = new ConnectionManager())
        {
            // Create the connection and assign here.
            // See (2).
            cm.connection = ...

            // Create the command to call sp_setapprole here.
            using (SqlCommand command = ...)
            {
                // Execute the command.
                command.ExecuteNonQuery();

                // Return the connection, but call release
                // so the connection is still live on return.
                return cm.Release();
            }
        }
    }
}
  1. Вы создадите SqlCommand, который соответствует вызову хранимой процедуры sp_setapprole. Вы также можете сгенерировать файл cookie и сохранить его в частной переменной-члене.
  2. Здесь вы создаете свое соединение.

Тогда клиентский код выглядит так:

using (ConnectionManager cm = ConnectionManager.Create())
{
    // Get the SqlConnection for use.
    // No need for a using statement, when Dispose is
    // called on the connection manager, the connection will be
    // closed.
    SqlConnection connection = cm.Connection;

    // Use connection appropriately.
}
person casperOne    schedule 17.02.2009
comment
Я вызываю unsetapprole, этот вопрос о том, можно ли обнаружить сценарий, в котором он не был вызван. - person Ed Guiness; 17.02.2009

Нет, это невозможно.

person Ed Guiness    schedule 25.08.2009

Это немного грязно, но если ваш первоначальный пользователь имеет права на VIEW SERVER STATE, select * from sys.sysprocesses вернет все процессы, когда роль не активна, и одну строку для текущего процесса, когда она активна.

person Ed Green    schedule 14.09.2015