From 63c4a0e8b6ac7e3ab4d8f8072cff857baf35fa05 Mon Sep 17 00:00:00 2001 From: campersau Date: Tue, 24 Mar 2026 19:51:56 +0100 Subject: [PATCH 01/10] Support SqlBatchCommand.CommandBehavior --- .../Data/SqlClient/SqlCommand.Batch.cs | 2 +- .../Data/SqlClient/SqlCommand.Reader.cs | 8 +-- .../SQL/Batch/BatchTests.netcore.cs | 59 +++++++++++++++++-- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs index 7b6d16a420..d4269cd6b1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs @@ -37,7 +37,7 @@ internal void AddBatchCommand(SqlBatchCommand batchCommand) { // All batch sql statements must be executed inside sp_executesql, including those // without parameters - BuildExecuteSql(CommandBehavior.Default, commandText, batchCommand.Parameters, ref rpc); + BuildExecuteSql(batchCommand.CommandBehavior, commandText, batchCommand.Parameters, ref rpc); } _RPCList.Add(rpc); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs index d9f6c33233..52cbfe4d66 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs @@ -463,13 +463,13 @@ private void BuildExecuteSql( SqlParameter sqlParam; // @batch_text - commandText ??= GetCommandText(behavior); + string text = GetCommandText(behavior) + GetOptionsResetString(behavior); sqlParam = rpc.systemParams[0]; - sqlParam.SqlDbType = (commandText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT + sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT ? SqlDbType.NVarChar : SqlDbType.NText; - sqlParam.Size = commandText.Length; - sqlParam.Value = commandText; + sqlParam.Size = text.Length; + sqlParam.Value = text; sqlParam.Direction = ParameterDirection.Input; // @batch_params diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs index 175e81521b..c9bb066f56 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -15,6 +15,55 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class BatchTests { + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void SqlCommandGetColumnSchemaKeyInfo() + { + using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + { + connection.Open(); + + using (var cmd = connection.CreateCommand()) + { + cmd.CommandText = "SELECT * FROM Categories"; + + using var reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo); + + Assert.False(reader.Read()); + + var schema = reader.GetColumnSchema(); + + Assert.Equal(4, schema.Count); + Assert.True(schema[0].IsKey); + } + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void SqlBatchCommandGetColumnSchemaKeyInfo() + { + using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + { + connection.Open(); + + using (var batch = connection.CreateBatch()) + { + var cmd = new SqlBatchCommand(); + cmd.CommandText = "SELECT * FROM Categories"; + cmd.CommandBehavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo; + + batch.BatchCommands.Add(cmd); + + using var reader = batch.ExecuteReader(); + + Assert.False(reader.Read()); + + var schema = reader.GetColumnSchema(); + + Assert.Equal(4, schema.Count); + Assert.True(schema[0].IsKey); + } + } + } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void MissingCommandTextThrows() @@ -123,14 +172,14 @@ public static void MixedBatchSupported() using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (var batch = new SqlBatch - { - Connection = connection, - BatchCommands = + { + Connection = connection, + BatchCommands = { new SqlBatchCommand("select @@SPID", CommandType.Text), new SqlBatchCommand("sp_help", CommandType.StoredProcedure, new List { new("@objname", "sys.indexes") }) } - }) + }) { connection.RetryLogicProvider = prov; connection.Open(); From 2a6a1ce7c9a09c66e21aa79ebc90c4234efaeb7e Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 1 Apr 2026 13:08:19 +0200 Subject: [PATCH 02/10] Support SqlBatch ExecuteReader CommandBehavior --- .../src/Microsoft/Data/SqlClient/SqlBatch.cs | 24 +++- .../Data/SqlClient/SqlCommand.Reader.cs | 4 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- .../SQL/Batch/BatchTests.netcore.cs | 108 ++++++++++-------- .../Microsoft/Data/SqlClient/SqlBatchTest.cs | 39 +++++++ 5 files changed, 121 insertions(+), 56 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs index 7171bdbfe3..f496551f80 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -185,13 +185,22 @@ public SqlDataReader ExecuteReader() return _batchCommand.ExecuteReaderAsync(cancellationToken); } /// - protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => ExecuteReader(); + protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) + { + ValidateCommandBehavior(nameof(ExecuteDbDataReader), behavior); + + CheckDisposed(); + SetupBatchCommandExecute(); + return _batchCommand.ExecuteReader(behavior); + } /// protected override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { + ValidateCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior); + CheckDisposed(); SetupBatchCommandExecute(); - return _batchCommand.ExecuteReaderAsync(cancellationToken) + return _batchCommand.ExecuteReaderAsync(behavior, cancellationToken) .ContinueWith((result) => { if (result.IsFaulted) @@ -238,6 +247,15 @@ private void SetupBatchCommandExecute() } _batchCommand.SetBatchRPCModeReadyToExecute(); } + + internal static void ValidateCommandBehavior(string method, CommandBehavior behavior) + { + if (0 != (behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection))) + { + ADP.ValidateCommandBehavior(behavior); + throw ADP.NotSupportedCommandBehavior(behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection), method); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs index 52cbfe4d66..fc5e904e4e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs @@ -463,7 +463,7 @@ private void BuildExecuteSql( SqlParameter sqlParam; // @batch_text - string text = GetCommandText(behavior) + GetOptionsResetString(behavior); + string text = GetCommandText(behavior); sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT ? SqlDbType.NVarChar @@ -1383,7 +1383,7 @@ private SqlDataReader RunExecuteReaderTds( $"Command Text '{CommandText}'"); } - string text = GetCommandText(cmdBehavior) + GetOptionsResetString(cmdBehavior); + string text = GetCommandText(cmdBehavior); // If the query requires enclave computations, pass the enclave package in the // SqlBatch TDS stream diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index 71845ddeb9..f411068572 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -2566,7 +2566,7 @@ private string GetCommandText(CommandBehavior behavior) // to the sproc. Debug.Assert(CommandType is CommandType.Text, "invalid call to GetCommandText for stored proc!"); - return GetOptionsSetString(behavior) + CommandText; + return GetOptionsSetString(behavior) + CommandText + GetOptionsResetString(behavior); } private SqlParameterCollection GetCurrentParameterCollection() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs index c9bb066f56..04fe171ce1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs @@ -15,56 +15,6 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class BatchTests { - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public static void SqlCommandGetColumnSchemaKeyInfo() - { - using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) - { - connection.Open(); - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "SELECT * FROM Categories"; - - using var reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo); - - Assert.False(reader.Read()); - - var schema = reader.GetColumnSchema(); - - Assert.Equal(4, schema.Count); - Assert.True(schema[0].IsKey); - } - } - } - - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public static void SqlBatchCommandGetColumnSchemaKeyInfo() - { - using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) - { - connection.Open(); - - using (var batch = connection.CreateBatch()) - { - var cmd = new SqlBatchCommand(); - cmd.CommandText = "SELECT * FROM Categories"; - cmd.CommandBehavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo; - - batch.BatchCommands.Add(cmd); - - using var reader = batch.ExecuteReader(); - - Assert.False(reader.Read()); - - var schema = reader.GetColumnSchema(); - - Assert.Equal(4, schema.Count); - Assert.True(schema[0].IsKey); - } - } - } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void MissingCommandTextThrows() { @@ -680,6 +630,64 @@ public static async Task ExecuteReaderAsyncMultiple() Assert.Equal(10, resultRowCount); } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void ExecuteReaderCommandCommandBehaviorSchemaOnlyKeyInfo() + { + System.Collections.ObjectModel.ReadOnlyCollection schema; + + using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlBatch batch = new SqlBatch(conn)) + { + conn.Open(); + + var cmd = new SqlBatchCommand("SELECT * FROM Categories"); + cmd.CommandBehavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo; + batch.BatchCommands.Add(cmd); + + using var reader = batch.ExecuteReader(); + + Assert.False(reader.Read()); + + schema = reader.GetColumnSchema(); + } + + Assert.Equal(4, schema.Count); + Assert.True(schema[0].IsKey); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void ExecuteReaderCommandBehaviorCloseConnection() + { + int resultSetCount = 0; + int resultRowCount = 0; + + using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlBatch batch = new SqlBatch(conn)) + { + conn.Open(); + + batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1")); + batch.BatchCommands.Add(new SqlBatchCommand("SELECT 2")); + + using (var reader = batch.ExecuteReader(CommandBehavior.CloseConnection)) + { + do + { + resultSetCount += 1; + while (reader.Read()) + { + resultRowCount += 1; + } + } while (reader.NextResult()); + } + + Assert.Equal(ConnectionState.Closed, conn.State); + } + + Assert.Equal(2, resultSetCount); + Assert.Equal(2, resultRowCount); + } + private static SqlParameter CreateParameter(string name, SqlDbType type, T value, ParameterDirection direction = ParameterDirection.Input) { var parameter = new SqlParameter(name, type); diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs new file mode 100644 index 0000000000..eef6872add --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET + +using System; +using System.Data; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests; + +/// +/// Provides unit tests for verifying the behavior of the SqlBatch class. +/// +public class SqlBatchTest +{ + /// + /// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when an invalid CommandBehavior is specified. + /// + [Fact] + public void InvalidCommandBehaviorValidateCommandBehavior_Throws() + { + ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", (CommandBehavior)64)); + Assert.Contains("CommandBehavior", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when a valid but unsupported CommandBehavior is specified. + /// + [Fact] + public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws() + { + ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", CommandBehavior.KeyInfo)); + Assert.Contains("not supported", ex.Message, StringComparison.OrdinalIgnoreCase); + } +} + +#endif From fe1ee31f6198cec79f68d172d3029ef4d5bd3845 Mon Sep 17 00:00:00 2001 From: campersau Date: Tue, 21 Apr 2026 19:46:04 +0200 Subject: [PATCH 03/10] Update tests --- .../tests/ManualTests/SQL/Batch/BatchTests.cs | 44 +++++++++++++++++-- .../Microsoft/Data/SqlClient/SqlBatchTest.cs | 4 -- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs index 4ceaf82e76..86fdf41f64 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs @@ -13,6 +13,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class BatchTests { + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void MissingCommandTextThrows() { @@ -128,14 +129,14 @@ public static void MixedBatchSupported() using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) using (var batch = new SqlBatch - { - Connection = connection, - BatchCommands = + { + Connection = connection, + BatchCommands = { new SqlBatchCommand("select @@SPID", CommandType.Text), new SqlBatchCommand("sp_help", CommandType.StoredProcedure, new List { new("@objname", "sys.indexes") }) } - }) + }) { connection.RetryLogicProvider = prov; connection.Open(); @@ -673,6 +674,7 @@ public static void ExecuteReaderCommandCommandBehaviorSchemaOnlyKeyInfo() Assert.True(schema[0].IsKey); } +#if NET [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void ExecuteReaderCommandBehaviorCloseConnection() { @@ -706,6 +708,40 @@ public static void ExecuteReaderCommandBehaviorCloseConnection() Assert.Equal(2, resultRowCount); } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static async Task ExecuteReaderAsyncCommandBehaviorCloseConnection() + { + int resultSetCount = 0; + int resultRowCount = 0; + + await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) + await using (SqlBatch batch = new SqlBatch(conn)) + { + await conn.OpenAsync(); + + batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1")); + batch.BatchCommands.Add(new SqlBatchCommand("SELECT 2")); + + using (var reader = await batch.ExecuteReaderAsync(CommandBehavior.CloseConnection)) + { + do + { + resultSetCount += 1; + while (await reader.ReadAsync()) + { + resultRowCount += 1; + } + } while (await reader.NextResultAsync()); + } + + Assert.Equal(ConnectionState.Closed, conn.State); + } + + Assert.Equal(2, resultSetCount); + Assert.Equal(2, resultRowCount); + } +#endif + private static SqlParameter CreateParameter(string name, SqlDbType type, T value, ParameterDirection direction = ParameterDirection.Input) { var parameter = new SqlParameter(name, type); diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs index eef6872add..97ac12a32e 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#if NET - using System; using System.Data; using Xunit; @@ -35,5 +33,3 @@ public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws() Assert.Contains("not supported", ex.Message, StringComparison.OrdinalIgnoreCase); } } - -#endif From 5ba1a6c6899f92a135bea4d6d4bfc98fc7bca130 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 13 May 2026 09:42:27 +0200 Subject: [PATCH 04/10] add documentation to ValidateExecuteCommandBehavior --- .../src/Microsoft/Data/SqlClient/SqlBatch.cs | 22 ++++++++++++++++--- .../Microsoft/Data/SqlClient/SqlBatchTest.cs | 12 +++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs index 5da858ec5a..5d83efcacd 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs @@ -295,7 +295,7 @@ Task ExecuteReaderAsync(CancellationToken cancellationToken = def #endif DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { - ValidateCommandBehavior(nameof(ExecuteDbDataReader), behavior); + ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReader), behavior); CheckDisposed(); SetupBatchCommandExecute(); @@ -311,7 +311,7 @@ DbDataReader ExecuteDbDataReader(CommandBehavior behavior) #endif Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - ValidateCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior); + ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior); CheckDisposed(); SetupBatchCommandExecute(); @@ -360,7 +360,23 @@ private void SetupBatchCommandExecute() _batchCommand.SetBatchRPCModeReadyToExecute(); } - internal static void ValidateCommandBehavior(string method, CommandBehavior behavior) + /// + /// Validates that the provided is compatible with . + /// + /// The name of the calling method for error reporting. + /// The behavior flags to validate. + /// + /// + /// Only and + /// are supported at the batch level. + /// + /// + /// To apply other behaviors (such as or ), + /// they must be set on individual instances within the batch. + /// + /// + /// Thrown when unsupported behavior flags are detected. + internal static void ValidateExecuteCommandBehavior(string method, CommandBehavior behavior) { if (0 != (behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection))) { diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs index 97ac12a32e..ebed579cd2 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBatchTest.cs @@ -14,22 +14,22 @@ namespace Microsoft.Data.SqlClient.UnitTests; public class SqlBatchTest { /// - /// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when an invalid CommandBehavior is specified. + /// Verifies that SqlBatch.ValidateExecuteCommandBehavior throws an ArgumentOutOfRangeException when an invalid CommandBehavior is specified. /// [Fact] - public void InvalidCommandBehaviorValidateCommandBehavior_Throws() + public void InvalidCommandBehaviorValidateExecuteCommandBehavior_Throws() { - ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", (CommandBehavior)64)); + ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateExecuteCommandBehavior("ExecuteNonQuery", (CommandBehavior)64)); Assert.Contains("CommandBehavior", ex.Message, StringComparison.OrdinalIgnoreCase); } /// - /// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when a valid but unsupported CommandBehavior is specified. + /// Verifies that SqlBatch.ValidateExecuteCommandBehavior throws an ArgumentOutOfRangeException when a valid but unsupported CommandBehavior is specified. /// [Fact] - public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws() + public void NotSupportedCommandBehaviorValidateExecuteCommandBehavior_Throws() { - ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", CommandBehavior.KeyInfo)); + ArgumentOutOfRangeException ex = Assert.Throws(() => SqlBatch.ValidateExecuteCommandBehavior("ExecuteNonQuery", CommandBehavior.KeyInfo)); Assert.Contains("not supported", ex.Message, StringComparison.OrdinalIgnoreCase); } } From 1731b086f1f1ec59de04d838ff415489572383e5 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 13 May 2026 10:17:27 +0200 Subject: [PATCH 05/10] support ExecuteReader(Async)(CommandBehavior) on .NET Framework --- .../Microsoft.Data.SqlClient/SqlBatch.xml | 82 +++- .../ref/Microsoft.Data.SqlClient.cs | 354 +++++++++--------- .../src/Microsoft/Data/SqlClient/SqlBatch.cs | 70 ++-- .../tests/ManualTests/SQL/Batch/BatchTests.cs | 4 +- 4 files changed, 300 insertions(+), 210 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml index 40f34cba48..635d378e52 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml @@ -1,4 +1,4 @@ - + @@ -179,7 +179,7 @@ The list of contained in the batch in a . - + Sends the to the and builds a . @@ -232,7 +232,83 @@ - + + + An instance of , specifying options for batch execution and data retrieval. + + + Sends the to the and builds a . + + + + The following example creates a and a , then adds multiple objects to the batch. It then executes the batch, creating a . The example reads through the results of the batch commands, writing them to the console. Finally, the example closes the and then the as the using blocks fall out of scope. + + + using Microsoft.Data.SqlClient; + + class Program + { + static void Main() + { + string str = "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI;Encrypt=False"; + RunBatch(str); + } + + static void RunBatch(string connString) + { + using var connection = new SqlConnection(connString); + connection.Open(); + + using var batch = new SqlBatch(connection); + + const int count = 10; + const string parameterName = "parameter"; + for (int i = 0; i < count; i++) + { + var batchCommand = new SqlBatchCommand($"SELECT @{parameterName} as value"); + batchCommand.Parameters.Add(new SqlParameter(parameterName, i)); + batch.BatchCommands.Add(batchCommand); + } + + var results = new List<int>(count); + using (SqlDataReader reader = batch.ExecuteReader()) + { + do + { + while (reader.Read()) + { + results.Add(reader.GetFieldValue<int>(0)); + } + } while (reader.NextResult()); + } + Console.WriteLine(string.Join(", ", results)); + } + } + + + + + A token to cancel the asynchronous operation. + + An asynchronous version of , which sends the to the and builds a . + Exceptions will be reported via the returned Task object. + + A task representing the asynchronous operation. + + An error occurred while executing the batch. + + + The value is invalid. + + + The cancellation token was canceled. This exception is stored into the returned task. + + + + + An instance of , specifying options for batch execution and data retrieval. + A token to cancel the asynchronous operation. An asynchronous version of , which sends the to the and builds a . diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs index e52cbdc8d3..5306b4fb2a 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs @@ -143,39 +143,51 @@ public class SqlBatch : System.IDisposable, System.IAsyncDisposable public override System.Threading.Tasks.Task ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; #else public System.Threading.Tasks.Task ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #endif - /// +#endif + /// public Microsoft.Data.SqlClient.SqlDataReader ExecuteReader() => throw null; - /// - #if NET + /// +#if NET + public new Microsoft.Data.SqlClient.SqlDataReader ExecuteReader(System.Data.CommandBehavior behavior) => throw null; +#else + public Microsoft.Data.SqlClient.SqlDataReader ExecuteReader(System.Data.CommandBehavior behavior) => throw null; +#endif + /// +#if NET public new System.Threading.Tasks.Task ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #else +#else public System.Threading.Tasks.Task ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #endif +#endif + /// +#if NET + public new System.Threading.Tasks.Task ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null; +#else + public System.Threading.Tasks.Task ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null; +#endif /// - #if NET +#if NET public override object ExecuteScalar() => throw null; - #else +#else public object ExecuteScalar() => throw null; - #endif +#endif /// - #if NET +#if NET public override System.Threading.Tasks.Task ExecuteScalarAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #else +#else public System.Threading.Tasks.Task ExecuteScalarAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #endif +#endif /// - #if NET +#if NET public override void Prepare() => throw null; - #else +#else public void Prepare() => throw null; - #endif +#endif /// - #if NET +#if NET public override System.Threading.Tasks.Task PrepareAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #else +#else public System.Threading.Tasks.Task PrepareAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - #endif +#endif } /// @@ -190,49 +202,49 @@ public class SqlBatchCommand /// public SqlBatchCommand(string commandText, System.Data.CommandType commandType = System.Data.CommandType.Text, System.Collections.Generic.IEnumerable parameters = null, Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting columnEncryptionSetting = Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting.UseConnectionSetting) { throw null; } /// - #if NET +#if NET public new Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } } - #else +#else public Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } } - #endif +#endif /// - #if NET +#if NET public override bool CanCreateParameter { get { throw null; } } - #else +#else public bool CanCreateParameter { get { throw null; } set { } } - #endif +#endif /// - #if NET +#if NET public override string CommandText { get { throw null; } set { } } - #else +#else public string CommandText { get { throw null; } set { } } - #endif +#endif /// - #if NET +#if NET public override System.Data.CommandType CommandType { get { throw null; } set { } } - #else +#else public System.Data.CommandType CommandType { get { throw null; } set { } } - #endif +#endif /// public System.Data.CommandBehavior CommandBehavior { get { throw null; } set { } } /// - #if NET +#if NET public override System.Data.Common.DbParameter CreateParameter() => throw null; - #else +#else public System.Data.Common.DbParameter CreateParameter() => throw null; - #endif +#endif /// - #if NET +#if NET public override int RecordsAffected { get { throw null; } } - #else +#else public int RecordsAffected { get { throw null; } } - #endif +#endif /// - #if NET +#if NET protected override System.Data.Common.DbParameterCollection DbParameterCollection => throw null; - #else +#else protected virtual System.Data.Common.DbParameterCollection DbParameterCollection => throw null; - #endif +#endif /// public Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting ColumnEncryptionSetting { get { throw null; } set { } } @@ -246,87 +258,87 @@ public class SqlBatchCommandCollection : System.Collections.Generic.IList - #if NET +#if NET public override int Count => throw null; - #else +#else public int Count => throw null; - #endif +#endif /// - #if NET +#if NET public override bool IsReadOnly => throw null; - #else +#else public bool IsReadOnly => throw null; - #endif +#endif System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null; - #if NET +#if NET /// public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null; - #endif +#endif /// public void Add(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; - #if NET +#if NET /// public override void Add(System.Data.Common.DbBatchCommand item) => throw null; - #endif +#endif /// - #if NET +#if NET public override void Clear() => throw null; - #else +#else public void Clear() => throw null; - #endif +#endif /// public bool Contains(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; - #if NET +#if NET /// public override bool Contains(System.Data.Common.DbBatchCommand item) => throw null; - #endif +#endif /// public void CopyTo(Microsoft.Data.SqlClient.SqlBatchCommand[] array, int arrayIndex) => throw null; - #if NET +#if NET /// public override void CopyTo(System.Data.Common.DbBatchCommand[] array, int arrayIndex) => throw null; - #endif +#endif /// public int IndexOf(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; - #if NET +#if NET /// public override int IndexOf(System.Data.Common.DbBatchCommand item) => throw null; - #endif +#endif /// public void Insert(int index, Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; - #if NET +#if NET /// public override void Insert(int index, System.Data.Common.DbBatchCommand item) => throw null; - #endif +#endif /// public bool Remove(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; - #if NET +#if NET /// public override bool Remove(System.Data.Common.DbBatchCommand item) => throw null; - #endif +#endif /// - #if NET +#if NET public override void RemoveAt(int index) => throw null; - #else +#else public void RemoveAt(int index) => throw null; - #endif +#endif /// Microsoft.Data.SqlClient.SqlBatchCommand System.Collections.Generic.IList.this[int index] { get => throw null; set { } } /// - #if NET +#if NET public new Microsoft.Data.SqlClient.SqlBatchCommand this[int index] { get => throw null; set { } } - #else +#else public Microsoft.Data.SqlClient.SqlBatchCommand this[int index] { get => throw null; set { } } - #endif +#endif System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null; - #if NET +#if NET /// protected override System.Data.Common.DbBatchCommand GetBatchCommand(int index) => throw null; - #endif - #if NET +#endif +#if NET /// protected override void SetBatchCommand(int index, System.Data.Common.DbBatchCommand batchCommand) => throw null; - #endif +#endif } /// @@ -525,19 +537,19 @@ internal SqlClientFactory() { } /// public static readonly Microsoft.Data.SqlClient.SqlClientFactory Instance; - #if NET +#if NET /// public override bool CanCreateBatch { get { throw null; } } - #endif +#endif - #if NET +#if NET /// public override System.Data.Common.DbBatch CreateBatch() { throw null; } - #endif - #if NET +#endif +#if NET /// public override System.Data.Common.DbBatchCommand CreateBatchCommand() { throw null; } - #endif +#endif /// public override System.Data.Common.DbCommand CreateCommand() { throw null; } /// @@ -553,14 +565,14 @@ internal SqlClientFactory() { } /// public override System.Data.Common.DbParameter CreateParameter() { throw null; } - #if NETFRAMEWORK +#if NETFRAMEWORK /// public override System.Security.CodeAccessPermission CreatePermission(System.Security.Permissions.PermissionState state) { throw null; } - #endif +#endif - #if NETFRAMEWORK +#if NETFRAMEWORK object System.IServiceProvider.GetService(System.Type serviceType) { throw null; } - #endif +#endif } /// @@ -788,44 +800,44 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect public event System.Data.StatementCompletedEventHandler StatementCompleted { add { } remove { } } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteNonQuery() { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteNonQuery(System.AsyncCallback callback, object stateObject) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteReader() { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteReader(System.AsyncCallback callback, object stateObject) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteReader(System.AsyncCallback callback, object stateObject, System.Data.CommandBehavior behavior) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteReader(System.Data.CommandBehavior behavior) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteXmlReader() { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public System.IAsyncResult BeginExecuteXmlReader(System.AsyncCallback callback, object stateObject) { throw null; } /// public override void Cancel() { } @@ -889,11 +901,11 @@ public void ResetCommandTimeout() { } /// protected override void Dispose(bool disposing) { } - #if NETFRAMEWORK +#if NETFRAMEWORK /// [System.ComponentModel.DefaultValueAttribute(true)] public bool NotificationAutoEnlist { get { throw null; } set { } } - #endif +#endif } /// @@ -1003,28 +1015,28 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden /// object System.ICloneable.Clone() { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] - #endif +#endif public static System.TimeSpan ColumnEncryptionKeyCacheTtl { get { throw null; } set { } } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] - #endif +#endif public static bool ColumnEncryptionQueryMetadataCacheEnabled { get { throw null; } set { } } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] - #endif +#endif public static System.Collections.Generic.IDictionary> ColumnEncryptionTrustedMasterKeyPaths { get { throw null; } } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public int CommandTimeout { get { throw null; } } /// [System.ComponentModel.DefaultValueAttribute("")] - #pragma warning disable CS0618 +#pragma warning disable CS0618 [System.ComponentModel.RecommendedAsConfigurableAttribute(true)] - #pragma warning restore CS0618 +#pragma warning restore CS0618 [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] [System.ComponentModel.SettingsBindableAttribute(true)] public override string ConnectionString { get { throw null; } set { } } @@ -1099,21 +1111,21 @@ public event Microsoft.Data.SqlClient.SqlInfoMessageEventHandler InfoMessage { a public override System.Data.DataTable GetSchema(string collectionName) { throw null; } /// public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; } - #if NET +#if NET /// public override System.Threading.Tasks.Task GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public override System.Threading.Tasks.Task GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public override System.Threading.Tasks.Task GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; } - #else +#else /// public System.Threading.Tasks.Task GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public System.Threading.Tasks.Task GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public System.Threading.Tasks.Task GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; } - #endif +#endif /// public override void Open() { } /// @@ -1135,49 +1147,49 @@ public void ResetStatistics() { } /// public SqlRetryLogicBaseProvider RetryLogicProvider { get { throw null; } set { } } - #if NETFRAMEWORK +#if NETFRAMEWORK /// protected override System.Data.Common.DbProviderFactory DbProviderFactory { get { throw null; } } - #endif +#endif /// protected override void Dispose(bool disposing) { } - #if NETFRAMEWORK +#if NETFRAMEWORK /// public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) { } - #endif +#endif - #if NETFRAMEWORK +#if NETFRAMEWORK /// public override void EnlistTransaction(System.Transactions.Transaction transaction) { } - #endif +#endif - #if NET +#if NET /// public override bool CanCreateBatch { get { throw null; } } - #endif +#endif - #if NET +#if NET /// protected override System.Data.Common.DbBatch CreateDbBatch() => throw null; - #endif +#endif - #if NET +#if NET /// /// for internal test only /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedState { get { throw null; } } - #endif +#endif - #if NET +#if NET /// /// for internal test only /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedStateBeforeRedirect { get { throw null; } } - #endif +#endif } /// @@ -1450,28 +1462,28 @@ public override void Clear() { } /// public override bool TryGetValue(string keyword, out object value) { throw null; } - #if NETFRAMEWORK +#if NETFRAMEWORK /// [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DisplayNameAttribute("Connection Reset")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] [System.ObsoleteAttribute("ConnectionReset has been deprecated. SqlConnection will ignore the 'connection reset' keyword and always reset the connection.")] public bool ConnectionReset { get { throw null; } set { } } - #endif +#endif - #if NETFRAMEWORK +#if NETFRAMEWORK /// [System.ComponentModel.DisplayNameAttribute("Network Library")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string NetworkLibrary { get { throw null; } set { } } - #endif +#endif - #if NETFRAMEWORK +#if NETFRAMEWORK /// [System.ComponentModel.DisplayNameAttribute("Transparent Network IP Resolution")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool TransparentNetworkIPResolution { get { throw null; } set { } } - #endif +#endif } /// @@ -1531,7 +1543,7 @@ protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs va /// object System.ICloneable.Clone() { throw null; } - #if NETFRAMEWORK +#if NETFRAMEWORK /// protected override int AddToBatch(System.Data.IDbCommand command) { throw null; } /// @@ -1550,7 +1562,7 @@ protected override void ClearBatch() { } protected override void InitializeBatching() { } /// protected override void TerminateBatching() { } - #endif +#endif } /// @@ -1606,9 +1618,9 @@ public override void Close() { } /// public override System.Collections.IEnumerator GetEnumerator() { throw null; } /// - #if NET +#if NET [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] - #endif +#endif public override System.Type GetFieldType(int i) { throw null; } /// public override T GetFieldValue(int i) { throw null; } @@ -1629,9 +1641,9 @@ public override void Close() { } /// public override int GetOrdinal(string name) { throw null; } /// - #if NET +#if NET [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] - #endif +#endif public override System.Type GetProviderSpecificFieldType(int i) { throw null; } /// public override object GetProviderSpecificValue(int i) { throw null; } @@ -1715,19 +1727,19 @@ public override void Close() { } public sealed class SqlDependency { /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public SqlDependency() { } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public SqlDependency(Microsoft.Data.SqlClient.SqlCommand command) { } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public SqlDependency(Microsoft.Data.SqlClient.SqlCommand command, string options, int timeout) { } /// public bool HasChanges { get { throw null; } } @@ -1738,24 +1750,24 @@ public event Microsoft.Data.SqlClient.OnChangeEventHandler OnChange { add { } re /// public void AddCommandDependency(Microsoft.Data.SqlClient.SqlCommand command) { } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public static bool Start(string connectionString) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public static bool Start(string connectionString, string queue) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public static bool Stop(string connectionString) { throw null; } /// - #if NETFRAMEWORK +#if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] - #endif +#endif public static bool Stop(string connectionString, string queue) { throw null; } } @@ -1815,23 +1827,23 @@ public sealed class SqlException : System.Data.Common.DbException internal SqlException() { } private SqlException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - #if NET +#if NET /// public new Microsoft.Data.SqlClient.SqlBatchCommand BatchCommand { get { throw null; } } - #else +#else /// public Microsoft.Data.SqlClient.SqlBatchCommand BatchCommand { get { throw null; } } - #endif +#endif /// public byte Class { get { throw null; } } /// public System.Guid ClientConnectionId { get { throw null; } } - #if NET +#if NET /// protected override System.Data.Common.DbBatchCommand DbBatchCommand => throw null; - #endif +#endif /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] @@ -1849,11 +1861,11 @@ private SqlException(System.Runtime.Serialization.SerializationInfo info, System /// public byte State { get { throw null; } } /// - #if NET +#if NET [System.Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] - #elif NETFRAMEWORK +#elif NETFRAMEWORK [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)] - #endif +#endif public override void GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } /// public override string ToString() { throw null; } @@ -2063,9 +2075,9 @@ public void ResetSqlDbType() { } /// public override string ToString() { throw null; } - #if !NETFRAMEWORK +#if !NETFRAMEWORK internal class SqlParameterConverter { } - #endif +#endif } /// @@ -2148,17 +2160,17 @@ protected override void SetParameter(int index, System.Data.Common.DbParameter v /// protected override void SetParameter(string parameterName, System.Data.Common.DbParameter value) { } - #if NETFRAMEWORK +#if NETFRAMEWORK /// [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.ObsoleteAttribute("Add(String parameterName, Object value) has been deprecated. Use AddWithValue(String parameterName, Object value). http://go.microsoft.com/fwlink/?linkid=14202", false)] public Microsoft.Data.SqlClient.SqlParameter Add(string parameterName, object value) { throw null; } - #endif +#endif - #if NETFRAMEWORK +#if NETFRAMEWORK /// public override bool IsSynchronized { get { throw null; } } - #endif +#endif } /// @@ -2321,18 +2333,18 @@ protected override void Dispose(bool disposing) { } public override void Rollback() { } /// - #if NET +#if NET public override void Rollback(string transactionName) { } - #else +#else public void Rollback(string transactionName) { } - #endif +#endif /// - #if NET +#if NET public override void Save(string savePointName) { } - #else +#else public void Save(string savePointName) { } - #endif +#endif } /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs index 5d83efcacd..4a51d20798 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs @@ -266,34 +266,15 @@ SqlTransaction Transaction } } - /// - public SqlDataReader ExecuteReader() - { - CheckDisposed(); - SetupBatchCommandExecute(); - return _batchCommand.ExecuteReader(); - } + /// + public SqlDataReader ExecuteReader() => ExecuteReader(CommandBehavior.Default); - /// + /// public #if NET new #endif - Task ExecuteReaderAsync(CancellationToken cancellationToken = default) - { - CheckDisposed(); - SetupBatchCommandExecute(); - return _batchCommand.ExecuteReaderAsync(cancellationToken); - } - - /// - protected - #if NET - override - #else - virtual - #endif - DbDataReader ExecuteDbDataReader(CommandBehavior behavior) + SqlDataReader ExecuteReader(CommandBehavior behavior) { ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReader), behavior); @@ -302,34 +283,57 @@ DbDataReader ExecuteDbDataReader(CommandBehavior behavior) return _batchCommand.ExecuteReader(behavior); } - /// - protected + /// + public #if NET - override - #else - virtual + new + #endif + Task ExecuteReaderAsync(CancellationToken cancellationToken = default) => ExecuteReaderAsync(CommandBehavior.Default, cancellationToken); + + /// + public + #if NET + new #endif - Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) + Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken = default) { ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior); CheckDisposed(); SetupBatchCommandExecute(); return _batchCommand.ExecuteReaderAsync(behavior, cancellationToken) - .ContinueWith((result) => + .ContinueWith((result) => { if (result.IsFaulted) { throw result.Exception.InnerException; } return result.Result; - }, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default ); } + /// + protected + #if NET + override + #else + virtual + #endif + DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => ExecuteReader(behavior); + + /// + protected + #if NET + override + #else + virtual + #endif + async Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) => await ExecuteReaderAsync(behavior, cancellationToken); + private void CheckDisposed() { if (_batchCommand is null) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs index 86fdf41f64..e92f8470bd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.cs @@ -674,7 +674,6 @@ public static void ExecuteReaderCommandCommandBehaviorSchemaOnlyKeyInfo() Assert.True(schema[0].IsKey); } -#if NET [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void ExecuteReaderCommandBehaviorCloseConnection() { @@ -714,7 +713,7 @@ public static async Task ExecuteReaderAsyncCommandBehaviorCloseConnection() int resultSetCount = 0; int resultRowCount = 0; - await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) + using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) await using (SqlBatch batch = new SqlBatch(conn)) { await conn.OpenAsync(); @@ -740,7 +739,6 @@ public static async Task ExecuteReaderAsyncCommandBehaviorCloseConnection() Assert.Equal(2, resultSetCount); Assert.Equal(2, resultRowCount); } -#endif private static SqlParameter CreateParameter(string name, SqlDbType type, T value, ParameterDirection direction = ParameterDirection.Input) { From 102746150889d21df0920e4d06bce7b4c6408d65 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 13 May 2026 11:16:19 +0200 Subject: [PATCH 06/10] reverset GetCommand unification and call GetOptionsResetString manually --- .../src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs | 4 ++-- .../src/Microsoft/Data/SqlClient/SqlCommand.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs index fc5e904e4e..52cbfe4d66 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs @@ -463,7 +463,7 @@ private void BuildExecuteSql( SqlParameter sqlParam; // @batch_text - string text = GetCommandText(behavior); + string text = GetCommandText(behavior) + GetOptionsResetString(behavior); sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT ? SqlDbType.NVarChar @@ -1383,7 +1383,7 @@ private SqlDataReader RunExecuteReaderTds( $"Command Text '{CommandText}'"); } - string text = GetCommandText(cmdBehavior); + string text = GetCommandText(cmdBehavior) + GetOptionsResetString(cmdBehavior); // If the query requires enclave computations, pass the enclave package in the // SqlBatch TDS stream diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index 1512ebad59..1073bb137c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -2566,7 +2566,7 @@ private string GetCommandText(CommandBehavior behavior) // to the sproc. Debug.Assert(CommandType is CommandType.Text, "invalid call to GetCommandText for stored proc!"); - return GetOptionsSetString(behavior) + CommandText + GetOptionsResetString(behavior); + return GetOptionsSetString(behavior) + CommandText; } private SqlParameterCollection GetCurrentParameterCollection() From 8677c313121b172ca74b3354dc177e1db03431dc Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 13 May 2026 11:26:32 +0200 Subject: [PATCH 07/10] cleanup accidental whiltespace --- .../ref/Microsoft.Data.SqlClient.cs | 350 +++++++++--------- .../src/Microsoft/Data/SqlClient/SqlBatch.cs | 4 +- 2 files changed, 177 insertions(+), 177 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs index 5306b4fb2a..313fffec5d 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs @@ -143,51 +143,51 @@ public class SqlBatch : System.IDisposable, System.IAsyncDisposable public override System.Threading.Tasks.Task ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; #else public System.Threading.Tasks.Task ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#endif + #endif /// public Microsoft.Data.SqlClient.SqlDataReader ExecuteReader() => throw null; /// -#if NET + #if NET public new Microsoft.Data.SqlClient.SqlDataReader ExecuteReader(System.Data.CommandBehavior behavior) => throw null; -#else + #else public Microsoft.Data.SqlClient.SqlDataReader ExecuteReader(System.Data.CommandBehavior behavior) => throw null; -#endif + #endif /// -#if NET + #if NET public new System.Threading.Tasks.Task ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#else + #else public System.Threading.Tasks.Task ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#endif + #endif /// -#if NET + #if NET public new System.Threading.Tasks.Task ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null; -#else + #else public System.Threading.Tasks.Task ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null; -#endif + #endif /// -#if NET + #if NET public override object ExecuteScalar() => throw null; -#else + #else public object ExecuteScalar() => throw null; -#endif + #endif /// -#if NET + #if NET public override System.Threading.Tasks.Task ExecuteScalarAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#else + #else public System.Threading.Tasks.Task ExecuteScalarAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#endif + #endif /// -#if NET + #if NET public override void Prepare() => throw null; -#else + #else public void Prepare() => throw null; -#endif + #endif /// -#if NET + #if NET public override System.Threading.Tasks.Task PrepareAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#else + #else public System.Threading.Tasks.Task PrepareAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; -#endif + #endif } /// @@ -202,49 +202,49 @@ public class SqlBatchCommand /// public SqlBatchCommand(string commandText, System.Data.CommandType commandType = System.Data.CommandType.Text, System.Collections.Generic.IEnumerable parameters = null, Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting columnEncryptionSetting = Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting.UseConnectionSetting) { throw null; } /// -#if NET + #if NET public new Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } } -#else + #else public Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } } -#endif + #endif /// -#if NET + #if NET public override bool CanCreateParameter { get { throw null; } } -#else + #else public bool CanCreateParameter { get { throw null; } set { } } -#endif + #endif /// -#if NET + #if NET public override string CommandText { get { throw null; } set { } } -#else + #else public string CommandText { get { throw null; } set { } } -#endif + #endif /// -#if NET + #if NET public override System.Data.CommandType CommandType { get { throw null; } set { } } -#else + #else public System.Data.CommandType CommandType { get { throw null; } set { } } -#endif + #endif /// public System.Data.CommandBehavior CommandBehavior { get { throw null; } set { } } /// -#if NET + #if NET public override System.Data.Common.DbParameter CreateParameter() => throw null; -#else + #else public System.Data.Common.DbParameter CreateParameter() => throw null; -#endif + #endif /// -#if NET + #if NET public override int RecordsAffected { get { throw null; } } -#else + #else public int RecordsAffected { get { throw null; } } -#endif + #endif /// -#if NET + #if NET protected override System.Data.Common.DbParameterCollection DbParameterCollection => throw null; -#else + #else protected virtual System.Data.Common.DbParameterCollection DbParameterCollection => throw null; -#endif + #endif /// public Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting ColumnEncryptionSetting { get { throw null; } set { } } @@ -258,87 +258,87 @@ public class SqlBatchCommandCollection : System.Collections.Generic.IList -#if NET + #if NET public override int Count => throw null; -#else + #else public int Count => throw null; -#endif + #endif /// -#if NET + #if NET public override bool IsReadOnly => throw null; -#else + #else public bool IsReadOnly => throw null; -#endif + #endif System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null; -#if NET + #if NET /// public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null; -#endif + #endif /// public void Add(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; -#if NET + #if NET /// public override void Add(System.Data.Common.DbBatchCommand item) => throw null; -#endif + #endif /// -#if NET + #if NET public override void Clear() => throw null; -#else + #else public void Clear() => throw null; -#endif + #endif /// public bool Contains(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; -#if NET + #if NET /// public override bool Contains(System.Data.Common.DbBatchCommand item) => throw null; -#endif + #endif /// public void CopyTo(Microsoft.Data.SqlClient.SqlBatchCommand[] array, int arrayIndex) => throw null; -#if NET + #if NET /// public override void CopyTo(System.Data.Common.DbBatchCommand[] array, int arrayIndex) => throw null; -#endif + #endif /// public int IndexOf(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; -#if NET + #if NET /// public override int IndexOf(System.Data.Common.DbBatchCommand item) => throw null; -#endif + #endif /// public void Insert(int index, Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; -#if NET + #if NET /// public override void Insert(int index, System.Data.Common.DbBatchCommand item) => throw null; -#endif + #endif /// public bool Remove(Microsoft.Data.SqlClient.SqlBatchCommand item) => throw null; -#if NET + #if NET /// public override bool Remove(System.Data.Common.DbBatchCommand item) => throw null; -#endif + #endif /// -#if NET + #if NET public override void RemoveAt(int index) => throw null; -#else + #else public void RemoveAt(int index) => throw null; -#endif + #endif /// Microsoft.Data.SqlClient.SqlBatchCommand System.Collections.Generic.IList.this[int index] { get => throw null; set { } } /// -#if NET + #if NET public new Microsoft.Data.SqlClient.SqlBatchCommand this[int index] { get => throw null; set { } } -#else + #else public Microsoft.Data.SqlClient.SqlBatchCommand this[int index] { get => throw null; set { } } -#endif + #endif System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null; -#if NET + #if NET /// protected override System.Data.Common.DbBatchCommand GetBatchCommand(int index) => throw null; -#endif -#if NET + #endif + #if NET /// protected override void SetBatchCommand(int index, System.Data.Common.DbBatchCommand batchCommand) => throw null; -#endif + #endif } /// @@ -537,19 +537,19 @@ internal SqlClientFactory() { } /// public static readonly Microsoft.Data.SqlClient.SqlClientFactory Instance; -#if NET + #if NET /// public override bool CanCreateBatch { get { throw null; } } -#endif + #endif -#if NET + #if NET /// public override System.Data.Common.DbBatch CreateBatch() { throw null; } -#endif -#if NET + #endif + #if NET /// public override System.Data.Common.DbBatchCommand CreateBatchCommand() { throw null; } -#endif + #endif /// public override System.Data.Common.DbCommand CreateCommand() { throw null; } /// @@ -565,14 +565,14 @@ internal SqlClientFactory() { } /// public override System.Data.Common.DbParameter CreateParameter() { throw null; } -#if NETFRAMEWORK + #if NETFRAMEWORK /// public override System.Security.CodeAccessPermission CreatePermission(System.Security.Permissions.PermissionState state) { throw null; } -#endif + #endif -#if NETFRAMEWORK + #if NETFRAMEWORK object System.IServiceProvider.GetService(System.Type serviceType) { throw null; } -#endif + #endif } /// @@ -800,44 +800,44 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect public event System.Data.StatementCompletedEventHandler StatementCompleted { add { } remove { } } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteNonQuery() { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteNonQuery(System.AsyncCallback callback, object stateObject) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteReader() { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteReader(System.AsyncCallback callback, object stateObject) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteReader(System.AsyncCallback callback, object stateObject, System.Data.CommandBehavior behavior) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteReader(System.Data.CommandBehavior behavior) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteXmlReader() { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public System.IAsyncResult BeginExecuteXmlReader(System.AsyncCallback callback, object stateObject) { throw null; } /// public override void Cancel() { } @@ -901,11 +901,11 @@ public void ResetCommandTimeout() { } /// protected override void Dispose(bool disposing) { } -#if NETFRAMEWORK + #if NETFRAMEWORK /// [System.ComponentModel.DefaultValueAttribute(true)] public bool NotificationAutoEnlist { get { throw null; } set { } } -#endif + #endif } /// @@ -1015,28 +1015,28 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden /// object System.ICloneable.Clone() { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] -#endif + #endif public static System.TimeSpan ColumnEncryptionKeyCacheTtl { get { throw null; } set { } } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] -#endif + #endif public static bool ColumnEncryptionQueryMetadataCacheEnabled { get { throw null; } set { } } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.ComponentModel.DefaultValueAttribute(null)] -#endif + #endif public static System.Collections.Generic.IDictionary> ColumnEncryptionTrustedMasterKeyPaths { get { throw null; } } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public int CommandTimeout { get { throw null; } } /// [System.ComponentModel.DefaultValueAttribute("")] -#pragma warning disable CS0618 + #pragma warning disable CS0618 [System.ComponentModel.RecommendedAsConfigurableAttribute(true)] -#pragma warning restore CS0618 + #pragma warning restore CS0618 [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] [System.ComponentModel.SettingsBindableAttribute(true)] public override string ConnectionString { get { throw null; } set { } } @@ -1111,21 +1111,21 @@ public event Microsoft.Data.SqlClient.SqlInfoMessageEventHandler InfoMessage { a public override System.Data.DataTable GetSchema(string collectionName) { throw null; } /// public override System.Data.DataTable GetSchema(string collectionName, string[] restrictionValues) { throw null; } -#if NET + #if NET /// public override System.Threading.Tasks.Task GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public override System.Threading.Tasks.Task GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public override System.Threading.Tasks.Task GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; } -#else + #else /// public System.Threading.Tasks.Task GetSchemaAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public System.Threading.Tasks.Task GetSchemaAsync(string collectionName, System.Threading.CancellationToken cancellationToken = default) { throw null; } /// public System.Threading.Tasks.Task GetSchemaAsync(string collectionName, string[] restrictionValues, System.Threading.CancellationToken cancellationToken = default) { throw null; } -#endif + #endif /// public override void Open() { } /// @@ -1147,49 +1147,49 @@ public void ResetStatistics() { } /// public SqlRetryLogicBaseProvider RetryLogicProvider { get { throw null; } set { } } -#if NETFRAMEWORK + #if NETFRAMEWORK /// protected override System.Data.Common.DbProviderFactory DbProviderFactory { get { throw null; } } -#endif + #endif /// protected override void Dispose(bool disposing) { } -#if NETFRAMEWORK + #if NETFRAMEWORK /// public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) { } -#endif + #endif -#if NETFRAMEWORK + #if NETFRAMEWORK /// public override void EnlistTransaction(System.Transactions.Transaction transaction) { } -#endif + #endif -#if NET + #if NET /// public override bool CanCreateBatch { get { throw null; } } -#endif + #endif -#if NET + #if NET /// protected override System.Data.Common.DbBatch CreateDbBatch() => throw null; -#endif + #endif -#if NET + #if NET /// /// for internal test only /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedState { get { throw null; } } -#endif + #endif -#if NET + #if NET /// /// for internal test only /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedStateBeforeRedirect { get { throw null; } } -#endif + #endif } /// @@ -1462,28 +1462,28 @@ public override void Clear() { } /// public override bool TryGetValue(string keyword, out object value) { throw null; } -#if NETFRAMEWORK + #if NETFRAMEWORK /// [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DisplayNameAttribute("Connection Reset")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] [System.ObsoleteAttribute("ConnectionReset has been deprecated. SqlConnection will ignore the 'connection reset' keyword and always reset the connection.")] public bool ConnectionReset { get { throw null; } set { } } -#endif + #endif -#if NETFRAMEWORK + #if NETFRAMEWORK /// [System.ComponentModel.DisplayNameAttribute("Network Library")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string NetworkLibrary { get { throw null; } set { } } -#endif + #endif -#if NETFRAMEWORK + #if NETFRAMEWORK /// [System.ComponentModel.DisplayNameAttribute("Transparent Network IP Resolution")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool TransparentNetworkIPResolution { get { throw null; } set { } } -#endif + #endif } /// @@ -1543,7 +1543,7 @@ protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs va /// object System.ICloneable.Clone() { throw null; } -#if NETFRAMEWORK + #if NETFRAMEWORK /// protected override int AddToBatch(System.Data.IDbCommand command) { throw null; } /// @@ -1562,7 +1562,7 @@ protected override void ClearBatch() { } protected override void InitializeBatching() { } /// protected override void TerminateBatching() { } -#endif + #endif } /// @@ -1618,9 +1618,9 @@ public override void Close() { } /// public override System.Collections.IEnumerator GetEnumerator() { throw null; } /// -#if NET + #if NET [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] -#endif + #endif public override System.Type GetFieldType(int i) { throw null; } /// public override T GetFieldValue(int i) { throw null; } @@ -1641,9 +1641,9 @@ public override void Close() { } /// public override int GetOrdinal(string name) { throw null; } /// -#if NET + #if NET [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] -#endif + #endif public override System.Type GetProviderSpecificFieldType(int i) { throw null; } /// public override object GetProviderSpecificValue(int i) { throw null; } @@ -1727,19 +1727,19 @@ public override void Close() { } public sealed class SqlDependency { /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public SqlDependency() { } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public SqlDependency(Microsoft.Data.SqlClient.SqlCommand command) { } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public SqlDependency(Microsoft.Data.SqlClient.SqlCommand command, string options, int timeout) { } /// public bool HasChanges { get { throw null; } } @@ -1750,24 +1750,24 @@ public event Microsoft.Data.SqlClient.OnChangeEventHandler OnChange { add { } re /// public void AddCommandDependency(Microsoft.Data.SqlClient.SqlCommand command) { } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public static bool Start(string connectionString) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public static bool Start(string connectionString, string queue) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public static bool Stop(string connectionString) { throw null; } /// -#if NETFRAMEWORK + #if NETFRAMEWORK [System.Security.Permissions.HostProtectionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, ExternalThreading = true)] -#endif + #endif public static bool Stop(string connectionString, string queue) { throw null; } } @@ -1827,23 +1827,23 @@ public sealed class SqlException : System.Data.Common.DbException internal SqlException() { } private SqlException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } -#if NET + #if NET /// public new Microsoft.Data.SqlClient.SqlBatchCommand BatchCommand { get { throw null; } } -#else + #else /// public Microsoft.Data.SqlClient.SqlBatchCommand BatchCommand { get { throw null; } } -#endif + #endif /// public byte Class { get { throw null; } } /// public System.Guid ClientConnectionId { get { throw null; } } -#if NET + #if NET /// protected override System.Data.Common.DbBatchCommand DbBatchCommand => throw null; -#endif + #endif /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] @@ -1861,11 +1861,11 @@ private SqlException(System.Runtime.Serialization.SerializationInfo info, System /// public byte State { get { throw null; } } /// -#if NET + #if NET [System.Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] -#elif NETFRAMEWORK + #elif NETFRAMEWORK [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)] -#endif + #endif public override void GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } /// public override string ToString() { throw null; } @@ -2075,9 +2075,9 @@ public void ResetSqlDbType() { } /// public override string ToString() { throw null; } -#if !NETFRAMEWORK + #if !NETFRAMEWORK internal class SqlParameterConverter { } -#endif + #endif } /// @@ -2160,17 +2160,17 @@ protected override void SetParameter(int index, System.Data.Common.DbParameter v /// protected override void SetParameter(string parameterName, System.Data.Common.DbParameter value) { } -#if NETFRAMEWORK + #if NETFRAMEWORK /// [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.ObsoleteAttribute("Add(String parameterName, Object value) has been deprecated. Use AddWithValue(String parameterName, Object value). http://go.microsoft.com/fwlink/?linkid=14202", false)] public Microsoft.Data.SqlClient.SqlParameter Add(string parameterName, object value) { throw null; } -#endif + #endif -#if NETFRAMEWORK + #if NETFRAMEWORK /// public override bool IsSynchronized { get { throw null; } } -#endif + #endif } /// @@ -2333,18 +2333,18 @@ protected override void Dispose(bool disposing) { } public override void Rollback() { } /// -#if NET + #if NET public override void Rollback(string transactionName) { } -#else + #else public void Rollback(string transactionName) { } -#endif + #endif /// -#if NET + #if NET public override void Save(string savePointName) { } -#else + #else public void Save(string savePointName) { } -#endif + #endif } /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs index 4a51d20798..8412d3c7af 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs @@ -276,7 +276,7 @@ SqlTransaction Transaction #endif SqlDataReader ExecuteReader(CommandBehavior behavior) { - ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReader), behavior); + ValidateExecuteCommandBehavior(nameof(ExecuteReader), behavior); CheckDisposed(); SetupBatchCommandExecute(); @@ -297,7 +297,7 @@ SqlDataReader ExecuteReader(CommandBehavior behavior) #endif Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken = default) { - ValidateExecuteCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior); + ValidateExecuteCommandBehavior(nameof(ExecuteReaderAsync), behavior); CheckDisposed(); SetupBatchCommandExecute(); From 66af53baa5836cffb27d14cce988e664bacd8106 Mon Sep 17 00:00:00 2001 From: campersau Date: Tue, 19 May 2026 09:54:36 +0200 Subject: [PATCH 08/10] pass CommandBehavior.CloseConnection in example --- doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml index 635d378e52..40db09c514 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml @@ -272,7 +272,7 @@ } var results = new List<int>(count); - using (SqlDataReader reader = batch.ExecuteReader()) + using (SqlDataReader reader = batch.ExecuteReader(CommandBehavior.CloseConnection)) { do { From 58c268f779c42f96daf2df295215147241fd2650 Mon Sep 17 00:00:00 2001 From: campersau Date: Tue, 19 May 2026 09:56:46 +0200 Subject: [PATCH 09/10] PR feedback --- .../src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs index d4269cd6b1..da7d1e2ed7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Batch.cs @@ -37,7 +37,7 @@ internal void AddBatchCommand(SqlBatchCommand batchCommand) { // All batch sql statements must be executed inside sp_executesql, including those // without parameters - BuildExecuteSql(batchCommand.CommandBehavior, commandText, batchCommand.Parameters, ref rpc); + BuildExecuteSql(batchCommand.CommandBehavior, batchCommand.Parameters, ref rpc); } _RPCList.Add(rpc); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs index 52cbfe4d66..91ccb684bc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs @@ -442,7 +442,6 @@ private _SqlRPC BuildExecute(bool inSchema) // @TODO: Can we return the RPC here like BuildExecute does? private void BuildExecuteSql( CommandBehavior behavior, - string commandText, SqlParameterCollection parameters, ref _SqlRPC rpc) { @@ -463,7 +462,7 @@ private void BuildExecuteSql( SqlParameter sqlParam; // @batch_text - string text = GetCommandText(behavior) + GetOptionsResetString(behavior); + string text = GetCommandText(behavior); sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT ? SqlDbType.NVarChar @@ -1455,7 +1454,7 @@ private SqlDataReader RunExecuteReaderTds( else { Debug.Assert(_execType is EXECTYPE.UNPREPARED, "Invalid execType!"); - BuildExecuteSql(cmdBehavior, commandText: null, _parameters, ref rpc); + BuildExecuteSql(cmdBehavior, _parameters, ref rpc); } rpc.options = TdsEnums.RPC_NOMETADATA; From 06272e8b398e9c1fc7a349dfe73eec97ef05b497 Mon Sep 17 00:00:00 2001 From: campersau Date: Wed, 20 May 2026 12:50:07 +0200 Subject: [PATCH 10/10] update docs --- doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml | 8 +++++++- doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml index 40db09c514..d2fdff51d2 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml @@ -235,6 +235,7 @@ An instance of , specifying options for batch execution and data retrieval. + Only and are supported at the batch level. Sends the to the and builds a . @@ -308,6 +309,7 @@ An instance of , specifying options for batch execution and data retrieval. + Only and are supported at the batch level. A token to cancel the asynchronous operation. @@ -379,6 +381,7 @@ An instance of , specifying options for batch execution and data retrieval. + Only and are supported at the batch level. Executes the batch against its connection, returning a which can be used to access the results. @@ -402,7 +405,10 @@ When the batch returns multiple result sets from different commands, - One of the enumeration values that specifies options for batch execution and data retrieval. + + An instance of , specifying options for batch execution and data retrieval. + Only and are supported at the batch level. + A token to cancel the asynchronous operation. This implementation invokes the method and returns a completed task. The default implementation will return a cancelled task if passed an already cancelled cancellation token. This method accepts a cancellation token that can be used to request the operation to be cancelled early. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml index 8f0be84d25..5145571840 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml @@ -1,4 +1,4 @@ - + @@ -87,7 +87,8 @@ - One of the values, indicating options for statement execution and data retrieval. + An instance of , specifying options for statement execution and data retrieval. + Only and are supported at the statement level.