CREATE / ALTER / PRINT後のSystem.Data.SqlClient.SqlException

Sep 03 2020

私は他の2つの質問から来ており、この例外が発生する理由を理解しようとしています。

EntityFrameworkシード-> SqlException:接続をリセットすると、最初のログインとは異なる状態になります。ログインに失敗します。結果-in-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」ステートメントを実行しないようにする必要がありますか?どうして?

回答

2 DanGuzman Sep 06 2020 at 17:03

「接続をリセットすると、最初のログインとは異なる状態になります。ログインは失敗します。」というエラーが表示されます。これは、プールされた接続がデータベースの状態の変更(データベースの照合順序の変更)後に再利用されているためです。以下は、エラーにつながる内部で発生することです。

このコードが実行されるとき:

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

ADO.NETは、接続文字列とセキュリティコンテキストを照合することにより、既存のプールされた接続を探します。(CREATE DATABASEクエリからの)既存のプールされた接続の接続文字列が異なるため(のmaster代わりにデータベース)、何も見つかりませんTESTDB。次に、ADO.NETは、TCP / IP接続の確立、認証、およびSQLServerセッションの初期化を含む新しい接続を作成します。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コマンドを完全に削除します