首页 > 解决方案 > CREATE/ALTER/PRINT 之后的 System.Data.SqlClient.SqlException

问题描述

我来自其他两个问题,并试图了解为什么会发生此异常。

实体框架种子 -> SqlException:重置连接会导致与初始登录不同的状态。登录失败。 结果-a-dif

“重置连接”是什么意思?System.Data.SqlClient.SqlException (0x80131904)

此代码重现异常。

string dbName = "TESTDB";
Run("master", $"CREATE DATABASE [{dbName}]");
Run(dbName, $"ALTER DATABASE [{dbName}] COLLATE Latin1_General_100_CI_AS");
Run(dbName, "PRINT 'HELLO'");

void Run(string catalog, string script)
{
    var cnxStr = new SqlConnectionStringBuilder
    {
        DataSource = serverAndInstance,
        UserID = user,
        Password = password,
        InitialCatalog = catalog
    };

    using var cn = new SqlConnection(cnxStr.ToString());
    using var cm = cn.CreateCommand();
    cn.Open();
    cm.CommandText = script;
    cm.ExecuteNonQuery();
}

完整的堆栈跟踪是

Unhandled Exception: System.Data.SqlClient.SqlException: Resetting the connection results in a different state than the initial login. The login fails.
Login failed for user 'user'.
Cannot continue the execution because the session is in the kill state.
A severe error occurred on the current command.  The results, if any, should be discarded.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
...

如果我将第一个更改Run(dbName...Run("master"...它运行良好。所以它与ALTER DATABASE在同一个数据库的上下文中运行有关

“重置连接”是什么意思?为什么会话“处于终止状态”。? 我应该避免在同一个数据库中运行“ALTER”语句吗?为什么?

标签: c#.netsql-serverado.netsqlconnection

解决方案


错误“重置连接导致与初始登录不同的状态。登录失败。” 是由于在数据库状态更改(数据库排序规则更改)后重用了池连接。以下是导致错误的内部发生的情况。

当此代码运行时:

Run(dbName, $"ALTER DATABASE [{dbName}] COLLATE Latin1_General_100_CI_AS");

ADO.NET 通过匹配连接字符串和安全上下文来查找现有的池连接。找不到,因为现有池连接(来自CREATE DATABASE查询)的连接字符串不同(masterdatabase 而不是TESTDB)。ADO.NET 然后创建一个新连接,其中包括建立 TCP/IP 连接、身份验证和 SQL Server 会话初始化。查询在这个ALTER DATABASE新连接上运行。连接被释放时(超出using范围)被添加到连接池中。

然后运行:

Run(dbName, "PRINT 'HELLO'");

ADO.NET 找到现有的池TESTDB连接并使用它而不是实例化新连接。当PRINT命令发送到 SQL Server 时,TDS 请求包括一个重置连接标志,以指示它是一个重用的池连接。这会导致 SQL Server 在内部调用sp_reset_connection以执行清理工作,如回滚未提交的事务、删除临时表、注销、登录等),详见此处。但是,sp_reset_connection由于数据库排序规则更改,无法将连接恢复到初始排序规则,从而导致登录失败。

以下是一些避免错误的技巧。我建议选项3。

  1. SqlConnection.ClearAllPools()更改排序规则后调用静态方法

  2. 指定master而不是命令,TESTDB以便ALTER DATABASE重复使用现有的“主”池连接,而不是创建新连接。随后的PRINT命令将创建一个新的连接,TESTDB因为池中不存在一个。

  3. CREATE DATABASE指定语句的排序规则并ALTER DATABASE完全删除命令


推荐阅读