System.Data.SqlClient.SqlException 在 CREATE/ALTER/PRINT 之后

System.Data.SqlClient.SqlException after CREATE/ALTER/PRINT

我来自其他 2 个问题,我想了解为什么会出现此异常。

结果差异

What does "Resetting the connection" mean? 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" 语句吗?为什么?

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

当这段代码 运行s:

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

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

然后这个 运行s:

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

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

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

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

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

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