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时发生的。连接池在您第一次使用连接时调用此函数,但在此之前无法进入

所以我想你有四个选择:

  • 通过在连接字符串中添加“pooling=false;”来关闭连接池
  • 使用其他方式连接到SQL Server。有比ADO.Net更低级别的API,但坦率地说,这可能不值得费心
  • 正如casperOne所说,您可以修复代码以正确关闭连接
  • 捕获异常并重置连接池。但我不确定这会对其他打开的连接产生什么影响。下面的示例代码:
  • 类程序
    {
    静态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();
                }
            }
        }
    }
    
  • 您将创建对应于调用sp_setapprole存储过程的SqlCommand。您可以生成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.
    }
    
    您可以检查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