System.Data.SqlClient.SqlException หลังจาก CREATE / ALTER / PRINT
ฉันมาจากคำถามอื่นอีก 2 ข้อและกำลังพยายามทำความเข้าใจว่าเหตุใดจึงเกิดข้อยกเว้นนี้ขึ้น
Entity Framework seed -> 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();
}
stacktrace แบบเต็มคือ
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ฐานข้อมูลแทน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คำสั่งเพื่อให้มีอยู่ 'ต้นแบบ' การเชื่อมต่อ pooled ถูกนำกลับมาใช้แทนการสร้างการเชื่อมต่อใหม่PRINTคำสั่งที่ตามมาจะสร้างการเชื่อมต่อใหม่TESTDBเนื่องจากไม่มีอยู่ในพูลระบุการเปรียบเทียบใน
CREATE DATABASEคำสั่งและลบALTER DATABASEคำสั่งทั้งหมด