Обнаружение непригодных пулов SqlConnections
Когда я пытаюсь установить роль приложения в SqlConnection с помощью sp_setapprole, я иногда получаю следующую ошибку в журнале событий Windows...
Соединение было разорвано, потому что субъект, открывший его, впоследствии принял новый контекст безопасности, а затем попытался сбросить соединение в рамках своего олицетворенного контекста безопасности. Этот сценарий не поддерживается. См. "Обзор олицетворения" в Книгах онлайн.)
... и в моем приложении выдается соответствующее исключение.
Это пул соединений, и было время, когда пул соединений был несовместим с ролями приложений - фактически старый совет от Microsoft состоял в том, чтобы отключить пул соединений (!!), но с введением sp_unsetapprole теперь (теоретически) возможно очистите соединение, прежде чем возвращать его в бассейн.
Я полагаю, что эти ошибки возникают, когда (по неизвестным причинам) sp_unsetapprole не запускается на соединении до его закрытия и возврата в пул соединений. Процедура sp_approle обречена на сбой при возвращении этого соединения из пула.
Я могу поймать и обработать это исключение, но я бы предпочел обнаружить надвигающийся сбой и вообще избежать исключения (и сообщений в журнале событий).
Можно ли обнаружить проблему, не вызывая исключения?
Мысли или советы приветствуются.
4 ответа
Это происходит по логике, а не с большим опытом использования sp_setapprole, но не будет ли возможно проверить контекст безопасности перед выполнением вызова? Или, в качестве альтернативы, сначала проверьте разрешение безопасности и контекст?
Казалось бы, вы вызываете 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();
}
}
}
}
- Вы создадите SqlCommand, который соответствует вызову хранимой процедуры sp_setapprole. Вы можете сгенерировать cookie и сохранить его в закрытой переменной-члене.
- Здесь вы создаете свою связь.
Код клиента тогда выглядит так:
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.
}
Это немного грязно, но если ваш оригинальный пользователь имеет права на VIEW SERVER STATE
, select * from sys.sysprocesses
вернет все процессы, когда роль не активна, и одну строку для текущего процесса, если она есть.