C# 如何在异常发生之前检测导致异常的条件?
我没有运气,所以我制作了这个尽可能简单的测试用例来演示这个问题 在下面的代码中,在尝试使用连接之前是否可以检测到连接不可用C# 如何在异常发生之前检测导致异常的条件?,c#,sql-server,ado.net,connection-pooling,application-role,C#,Sql Server,Ado.net,Connection Pooling,Application Role,我没有运气,所以我制作了这个尽可能简单的测试用例来演示这个问题 在下面的代码中,在尝试使用连接之前是否可以检测到连接不可用 SqlConnection c = new SqlConnection(myConnString); c.Open(); // creates pool setAppRole(c); // OK c.Close(); // returns connection to pool c = new SqlConnection(m
SqlConnection c = new SqlConnection(myConnString);
c.Open(); // creates pool
setAppRole(c); // OK
c.Close(); // returns connection to pool
c = new SqlConnection(myConnString); // gets connection from pool
c.Open(); // ok... but wait for it...
// ??? How to detect KABOOM before it happens?
setAppRole(c); // KABOOM
KABOOM在Windows事件日志中显示为错误
连接已断开,因为打开连接的主体随后假定了新的安全上下文,然后尝试在其模拟的安全上下文下重置连接。不支持此方案。请参阅联机丛书中的“模拟概述”
…加上代码中的异常
setAppRole是在连接上设置应用程序角色的简单方法。与此类似
static void setAppRole(SqlConnection conn) {
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "exec sp_setapprole ";
cmd.CommandText += string.Format("@rolename='{0}'",myUser);
cmd.CommandText += string.Format(",@password='{0}'",myPassword);
cmd.ExecuteNonQuery();
}
}
在实际代码中,试图在关闭连接之前使用sp_unsetapprole,但不能始终保证这一点(继承的多线程错误应用)。在任何情况下,期望能够在引起kaboom之前检测到它似乎仍然是合理的。尝试将
sp_unsetapprole
(它真的是存储过程的名称吗?可能,sp_dropapprole
是正确的吗?)移动到setAppRole()
并在添加应用程序角色之前执行它。我不确定您的问题是否存在,但我认为如果您创建了新的连接对象而不是重用它们,则可以避免此问题。因此,与其这样做
c.Open();
blabla;
c.Close();
c.Open();
kaboom...
您将执行以下操作:
using (new SqlConnection ...)
{
c.Open();
blabla;
}
using (new SqlConnection ... )
{
c.Open();
no kaboom?
}
System.Data.ConnectionState.Broken
System.Data.ConnectionState.Closed
System.Data.ConnectionState.Connecting
System.Data.ConnectionState.Executing
System.Data.ConnectionState.Fetching
System.Data.ConnectionState.Open
(请原谅伪代码…我的eeepc上的键盘无法使用…无法清除所有连接池。SqlPools,清除还是什么
您可以简单地尝试捕获异常并创建一个新连接,这将强制池创建一个完整的新连接。简而言之,看起来您无法以任何简单的方式创建 我的第一个想法是运行以下SQL:
SELECT CASE WHEN USER = 'MyAppRole' THEN 1 ELSE 0 END
如果您使用SQLServerManagementStudio,这是可行的,但从C#代码运行时失败。问题是,在调用sp_setapprole时,您得到的错误没有发生,而实际上是在连接池调用sp_reset_connection时发生的。连接池在您第一次使用连接时调用此函数,但在此之前无法进入
所以我想你有四个选择:
类程序
{
静态void Main(字符串[]参数)
{
SqlConnection conn=newsqlconnection(“服务器=(本地);数据库=测试;UID=废弃;PWD=密码;”;
塞特帕勒(康涅狄格州);
康涅狄格州关闭();
塞特帕勒(康涅狄格州);
康涅狄格州关闭();
}
静态void setAppRole(SqlConnection conn)
{
对于(int i=0;i<2;i++)
{
conn.Open();
尝试
{
使用(IDbCommand cmd=conn.CreateCommand())
{
cmd.CommandType=CommandType.Text;
cmd.CommandText=“exec sp_setapprole”;
cmd.CommandText+=string.Format(“@rolename='{0}'”,“MyAppRole”);
cmd.CommandText+=string.Format(“,@password='{0}'”,“password1”);
cmd.ExecuteNonQuery();
}
}
catch(SqlException-ex)
{
如果(i==0&&ex.Number==0)
{
康涅狄格州关闭();
SqlConnection.ClearPool(conn);
继续;
}
其他的
{
投掷;
}
}
返回;
}
}
}
我也发布了这篇文章,以回应您之前的问题。调用sp_setapprole时,您应该在完成后调用sp_unsetapprole,我在这里提出的解决方案将帮助您:
似乎您正在调用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();
}
}
}
}
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.
}
您可以检查c.State(ConnectionState对象),它应该是以下内容之一:
using (new SqlConnection ...)
{
c.Open();
blabla;
}
using (new SqlConnection ... )
{
c.Open();
no kaboom?
}
System.Data.ConnectionState.Broken
System.Data.ConnectionState.Closed
System.Data.ConnectionState.Connecting
System.Data.ConnectionState.Executing
System.Data.ConnectionState.Fetching
System.Data.ConnectionState.Open
@edg:你在评论中说,“…只有当它到达实际服务器时,才会遇到msg引用中描述的安全问题” 这就指向了问题的根源:您遇到了安全问题,这似乎是不可避免的,因为调用代码使用了另一个身份,而不是用来打开连接的身份。这自然会生成一个安全日志条目 由于身份更改是经过设计的,因此解决方案可能是过滤安全日志。事件查看器有一个筛选器当前日志操作,可以按关键字或eventid进行筛选 +汤姆 它实际上是在连接池调用时发生的 sp_重置_连接。连接池在您第一次使用时调用此函数 一个连接,在它之前没有办法进入 基于,您可以尝试将“Connection Reset=False”添加到连接字符串中,作为“在sp_Reset_连接之前进入”的一种方式。(有关此设置的许多缺点的解释,请参见“” 你的问题很严重。建议的解决方法是t