-
Notifications
You must be signed in to change notification settings - Fork 322
Description
Describe the bug
In SqlDependencyListener, when an incoming XML message is parsed, sometimes these messages look like this:
<qn:QueryNotification xmlns:qn="http://schemas.microsoft.com/SQL/Notifications/QueryNotification" id="4579" type="change" source="timeout" info="none" database_id="9" sid="0x01050000000000051500000002AAFCB5FFBA57356D2C83B7E9030000"><qn:Message>52cdaaaf-f5e5-4342-8c10-dfec05cf4950;ec06ef71-ec94-4e56-a11e-c1905ebf4cf9</qn:Message></qn:QueryNotification>The info attribute is parsed at https://github.com/dotnet/SqlClient/blob/main/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs#L1133-L1167. However, the enum that the value is parsed into does not included a value "None", causing an exception to be thrown. It is ultimately handled by the containing try/catch, but there are a few issues with this:
- Exception flow and handling incurs a performance cost.
- The exception is System.ArgumentException and is thrown from mscorlib, which means that suppressing it in Visual Studio's Exception Settings causes all such exceptions to be supressed, making debugging more difficult when there are other AgumentExceptions thrown by mscorlib that I do want to break on.
To reproduce
This is a bit weird to reproduce. The first few notifications where will be of Info == Error. But sometimes, it will be "none" in the XML, which then throws and gets turned into Unknown.
Set exception settings in Visual Studio to break on all CLR exceptions.
// See https://aka.ms/new-console-template for more information
using Microsoft.Data.SqlClient;
var connectionString = "Server=localhost;Database=BugReproDb;Trusted_Connection=True;Command Timeout=5";
SqlDependency.Start(connectionString);
var connection = new SqlConnection(connectionString);
connection.Open();
var cmd1 = connection.CreateCommand();
cmd1.CommandText = "IF OBJECT_ID('TestTable') IS NULL CREATE TABLE TestTable (c1 int);";
cmd1.ExecuteNonQuery();
cmd1.Dispose();
Console.WriteLine("Usually the notification here will be 'Change Timeout Error'.");
Console.WriteLine("But sometimes, it will be 'Change Timeout Unknown'. This is the error.");
while (true)
{
var tcs = new TaskCompletionSource();
var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT c1 FROM dbo.TestTable";
var dependency = new SqlDependency(cmd, null, (int)1);
dependency.OnChange += (o, e) => {
Console.WriteLine($"{e.Type} {e.Source} {e.Info}");
if (e.Info == SqlNotificationInfo.Unknown)
{
Environment.Exit(0);
}
tcs.SetResult();
};
cmd.ExecuteNonQuery();
await tcs.Task;
cmd.Dispose();
}Expected behavior
SqlNotificationInfo.None is defined so exception flow isn't needed.
Further technical details
Microsoft.Data.SqlClient version: 3.0.1
.NET target: net6.0
SQL Server version: 15.0.2000.5
Operating system: Win 10
