System.Data.SqlClient.SqlException nach CREATE / ALTER / PRINT
Ich komme aus 2 anderen Fragen und versuche zu verstehen, warum diese Ausnahme auftritt.
Entity Framework-Startwert -> SqlException: Das Zurücksetzen der Verbindung führt zu einem anderen Status als die anfängliche Anmeldung. Die Anmeldung schlägt fehl. Ergebnisse in einem Unterschied
Was bedeutet "Verbindung zurücksetzen"? System.Data.SqlClient.SqlException (0x80131904)
Dieser Code gibt die Ausnahme wieder.
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();
}
Die vollständige Stapelverfolgung ist
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()
...
Wenn ich das erste ändere Run(dbName..., Run("master"...läuft es gut. Es hängt also damit zusammen, ALTER DATABASEim Kontext derselben Datenbank ausgeführt zu werden
Was bedeutet "Verbindung zurücksetzen"? Warum befindet sich die Sitzung "im Kill-Zustand"? ? Sollte ich vermeiden, "ALTER" -Anweisungen in derselben Datenbank auszuführen? Warum?
Antworten
Der Fehler "Das Zurücksetzen der Verbindung führt zu einem anderen Status als die anfängliche Anmeldung. Die Anmeldung schlägt fehl." ist darauf zurückzuführen, dass eine gepoolte Verbindung nach der Änderung des Datenbankstatus (Änderung der Datenbankkollatierung) wiederverwendet wird. Nachfolgend sehen Sie, was intern passiert und zu dem Fehler führt.
Wenn dieser Code ausgeführt wird:
Run(dbName, $"ALTER DATABASE [{dbName}] COLLATE Latin1_General_100_CI_AS");
ADO.NET sucht nach einer vorhandenen Poolverbindung, indem die Verbindungszeichenfolge und der Sicherheitskontext abgeglichen werden. Keine wurde gefunden, da sich die Verbindungszeichenfolge der vorhandenen gepoolten Verbindung (von der CREATE DATABASEAbfrage) unterscheidet ( masterDatenbank anstelle von TESTDB). ADO.NET erstellt dann eine neue Verbindung, die das Herstellen einer TCP / IP-Verbindung, die Authentifizierung und die Initialisierung der SQL Server-Sitzung umfasst. Die ALTER DATABASEAbfrage wird für diese neue Verbindung ausgeführt. Die Verbindung wird dem Verbindungspool hinzugefügt, wenn sie entsorgt wird (außerhalb des usingBereichs).
Dann läuft das:
Run(dbName, "PRINT 'HELLO'");
ADO.NET findet die vorhandene Poolverbindung TESTDBund verwendet diese, anstatt eine neue Verbindung zu instanziieren. Wenn der PRINTBefehl an SQL Server gesendet wird, enthält die TDS-Anforderung ein Flag zum Zurücksetzen der Verbindung, um anzuzeigen, dass es sich um eine wiederverwendete Poolverbindung handelt. Dies führt dazu, dass SQL Server intern aufgerufen wird sp_reset_connection, um Bereinigungsarbeiten wie das Zurücksetzen nicht festgeschriebener Transaktionen, das Löschen temporärer Tabellen, das Abmelden, das Anmelden usw. durchzuführen, wie hier beschrieben . Die sp_reset_connectionVerbindung kann jedoch aufgrund der Änderung der Datenbankkollatierung, die zu einem Anmeldefehler führt , nicht auf die ursprüngliche Sortierung zurückgesetzt werden.
Im Folgenden finden Sie einige Techniken, um den Fehler zu vermeiden. Ich schlage Option 3 vor.
Rufen Sie die statische
SqlConnection.ClearAllPools()Methode auf, nachdem Sie die Sortierung geändert habenGeben Sie
masteranstelle vonTESTDBfür denALTER DATABASEBefehl an, damit die vorhandene gepoolte 'Master'-Verbindung wiederverwendet wird, anstatt eine neue Verbindung zu erstellen. Der nachfolgendePRINTBefehl erstellt dann eine neue Verbindung für,TESTDBda keine im Pool vorhanden ist.Geben Sie die Sortierung in der
CREATE DATABASEAnweisung an und entfernen Sie denALTER DATABASEBefehl vollständig