c# - 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”语句吗?为什么?
解决方案
错误“重置连接导致与初始登录不同的状态。登录失败。” 是由于在数据库状态更改(数据库排序规则更改)后重用了池连接。以下是导致错误的内部发生的情况。
当此代码运行时:
Run(dbName, $"ALTER DATABASE [{dbName}] COLLATE Latin1_General_100_CI_AS");
ADO.NET 通过匹配连接字符串和安全上下文来查找现有的池连接。找不到,因为现有池连接(来自CREATE DATABASE
查询)的连接字符串不同(master
database 而不是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。
SqlConnection.ClearAllPools()
更改排序规则后调用静态方法指定
master
而不是命令,TESTDB
以便ALTER DATABASE
重复使用现有的“主”池连接,而不是创建新连接。随后的PRINT
命令将创建一个新的连接,TESTDB
因为池中不存在一个。CREATE DATABASE
指定语句的排序规则并ALTER DATABASE
完全删除命令
推荐阅读
- php - Laravel 中 Session::flash() 和 with() 的区别
- javascript - 选定范围识别
- r - 将嵌套列表转换为数据框/数据表
- azure - 天蓝色虚拟机和节点之间的区别
- mysql - 当 select 语句不输出任何行时,如何将数据插入到表中(并返回)?
- prolog - Prolog中偶数和奇数的两种实现之间的区别
- wpf - 如何使用 TestStack.White 模拟点击 WPF 中的动态 MenuItem?
- javascript - 如何在 NodeJs 中使用 express 从请求中提取正文?
- c++ - 定义期间变量的参数
- algorithm - Finding maximum in array in time of O(log(n))- with some assumptions