Skip to content

Commit 2a6a1ce

Browse files
committed
Support SqlBatch ExecuteReader CommandBehavior
1 parent 63c4a0e commit 2a6a1ce

5 files changed

Lines changed: 121 additions & 56 deletions

File tree

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBatch.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

@@ -185,13 +185,22 @@ public SqlDataReader ExecuteReader()
185185
return _batchCommand.ExecuteReaderAsync(cancellationToken);
186186
}
187187
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteDbDataReader/*'/>
188-
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => ExecuteReader();
188+
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
189+
{
190+
ValidateCommandBehavior(nameof(ExecuteDbDataReader), behavior);
191+
192+
CheckDisposed();
193+
SetupBatchCommandExecute();
194+
return _batchCommand.ExecuteReader(behavior);
195+
}
189196
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteDbDataReaderAsync/*'/>
190197
protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
191198
{
199+
ValidateCommandBehavior(nameof(ExecuteDbDataReaderAsync), behavior);
200+
192201
CheckDisposed();
193202
SetupBatchCommandExecute();
194-
return _batchCommand.ExecuteReaderAsync(cancellationToken)
203+
return _batchCommand.ExecuteReaderAsync(behavior, cancellationToken)
195204
.ContinueWith<DbDataReader>((result) =>
196205
{
197206
if (result.IsFaulted)
@@ -238,6 +247,15 @@ private void SetupBatchCommandExecute()
238247
}
239248
_batchCommand.SetBatchRPCModeReadyToExecute();
240249
}
250+
251+
internal static void ValidateCommandBehavior(string method, CommandBehavior behavior)
252+
{
253+
if (0 != (behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection)))
254+
{
255+
ADP.ValidateCommandBehavior(behavior);
256+
throw ADP.NotSupportedCommandBehavior(behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection), method);
257+
}
258+
}
241259
}
242260
}
243261

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Reader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ private void BuildExecuteSql(
463463
SqlParameter sqlParam;
464464

465465
// @batch_text
466-
string text = GetCommandText(behavior) + GetOptionsResetString(behavior);
466+
string text = GetCommandText(behavior);
467467
sqlParam = rpc.systemParams[0];
468468
sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT
469469
? SqlDbType.NVarChar
@@ -1383,7 +1383,7 @@ private SqlDataReader RunExecuteReaderTds(
13831383
$"Command Text '{CommandText}'");
13841384
}
13851385

1386-
string text = GetCommandText(cmdBehavior) + GetOptionsResetString(cmdBehavior);
1386+
string text = GetCommandText(cmdBehavior);
13871387

13881388
// If the query requires enclave computations, pass the enclave package in the
13891389
// SqlBatch TDS stream

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2566,7 +2566,7 @@ private string GetCommandText(CommandBehavior behavior)
25662566
// to the sproc.
25672567
Debug.Assert(CommandType is CommandType.Text,
25682568
"invalid call to GetCommandText for stored proc!");
2569-
return GetOptionsSetString(behavior) + CommandText;
2569+
return GetOptionsSetString(behavior) + CommandText + GetOptionsResetString(behavior);
25702570
}
25712571

25722572
private SqlParameterCollection GetCurrentParameterCollection()

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Batch/BatchTests.netcore.cs

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,56 +15,6 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests
1515
{
1616
public static class BatchTests
1717
{
18-
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
19-
public static void SqlCommandGetColumnSchemaKeyInfo()
20-
{
21-
using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString))
22-
{
23-
connection.Open();
24-
25-
using (var cmd = connection.CreateCommand())
26-
{
27-
cmd.CommandText = "SELECT * FROM Categories";
28-
29-
using var reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
30-
31-
Assert.False(reader.Read());
32-
33-
var schema = reader.GetColumnSchema();
34-
35-
Assert.Equal(4, schema.Count);
36-
Assert.True(schema[0].IsKey);
37-
}
38-
}
39-
}
40-
41-
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
42-
public static void SqlBatchCommandGetColumnSchemaKeyInfo()
43-
{
44-
using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString))
45-
{
46-
connection.Open();
47-
48-
using (var batch = connection.CreateBatch())
49-
{
50-
var cmd = new SqlBatchCommand();
51-
cmd.CommandText = "SELECT * FROM Categories";
52-
cmd.CommandBehavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
53-
54-
batch.BatchCommands.Add(cmd);
55-
56-
using var reader = batch.ExecuteReader();
57-
58-
Assert.False(reader.Read());
59-
60-
var schema = reader.GetColumnSchema();
61-
62-
Assert.Equal(4, schema.Count);
63-
Assert.True(schema[0].IsKey);
64-
}
65-
}
66-
}
67-
6818
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
6919
public static void MissingCommandTextThrows()
7020
{
@@ -680,6 +630,64 @@ public static async Task ExecuteReaderAsyncMultiple()
680630
Assert.Equal(10, resultRowCount);
681631
}
682632

633+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
634+
public static void ExecuteReaderCommandCommandBehaviorSchemaOnlyKeyInfo()
635+
{
636+
System.Collections.ObjectModel.ReadOnlyCollection<DbColumn> schema;
637+
638+
using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString))
639+
using (SqlBatch batch = new SqlBatch(conn))
640+
{
641+
conn.Open();
642+
643+
var cmd = new SqlBatchCommand("SELECT * FROM Categories");
644+
cmd.CommandBehavior = CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
645+
batch.BatchCommands.Add(cmd);
646+
647+
using var reader = batch.ExecuteReader();
648+
649+
Assert.False(reader.Read());
650+
651+
schema = reader.GetColumnSchema();
652+
}
653+
654+
Assert.Equal(4, schema.Count);
655+
Assert.True(schema[0].IsKey);
656+
}
657+
658+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
659+
public static void ExecuteReaderCommandBehaviorCloseConnection()
660+
{
661+
int resultSetCount = 0;
662+
int resultRowCount = 0;
663+
664+
using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString))
665+
using (SqlBatch batch = new SqlBatch(conn))
666+
{
667+
conn.Open();
668+
669+
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1"));
670+
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 2"));
671+
672+
using (var reader = batch.ExecuteReader(CommandBehavior.CloseConnection))
673+
{
674+
do
675+
{
676+
resultSetCount += 1;
677+
while (reader.Read())
678+
{
679+
resultRowCount += 1;
680+
}
681+
} while (reader.NextResult());
682+
}
683+
684+
Assert.Equal(ConnectionState.Closed, conn.State);
685+
}
686+
687+
Assert.Equal(2, resultSetCount);
688+
Assert.Equal(2, resultRowCount);
689+
}
690+
683691
private static SqlParameter CreateParameter<T>(string name, SqlDbType type, T value, ParameterDirection direction = ParameterDirection.Input)
684692
{
685693
var parameter = new SqlParameter(name, type);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if NET
6+
7+
using System;
8+
using System.Data;
9+
using Xunit;
10+
11+
namespace Microsoft.Data.SqlClient.UnitTests;
12+
13+
/// <summary>
14+
/// Provides unit tests for verifying the behavior of the SqlBatch class.
15+
/// </summary>
16+
public class SqlBatchTest
17+
{
18+
/// <summary>
19+
/// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when an invalid CommandBehavior is specified.
20+
/// </summary>
21+
[Fact]
22+
public void InvalidCommandBehaviorValidateCommandBehavior_Throws()
23+
{
24+
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", (CommandBehavior)64));
25+
Assert.Contains("CommandBehavior", ex.Message, StringComparison.OrdinalIgnoreCase);
26+
}
27+
28+
/// <summary>
29+
/// Verifies that SqlBatch.ValidateCommandBehavior throws an ArgumentOutOfRangeException when a valid but unsupported CommandBehavior is specified.
30+
/// </summary>
31+
[Fact]
32+
public void NotSupportedCommandBehaviorValidateCommandBehavior_Throws()
33+
{
34+
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => SqlBatch.ValidateCommandBehavior("ExecuteNonQuery", CommandBehavior.KeyInfo));
35+
Assert.Contains("not supported", ex.Message, StringComparison.OrdinalIgnoreCase);
36+
}
37+
}
38+
39+
#endif

0 commit comments

Comments
 (0)