Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 86 additions & 4 deletions doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<docs>
<docs>
<members name="SqlBatch">
<SqlBatch>
<example>
Expand Down Expand Up @@ -179,7 +179,7 @@
The list of <see cref="T:Microsoft.Data.SqlClient.SqlBatchCommand" /> contained in the batch in a <see cref="T:System.Collections.Generic.List`1" />.
</summary>
</Commands>
<ExecuteReader>
<ExecuteReader name="NoParameter">
<summary>
Sends the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Commands" /> to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Connection" /> and builds a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" />.
</summary>
Expand Down Expand Up @@ -232,7 +232,85 @@
</code>
</example>
</ExecuteReader>
<ExecuteReaderAsync>
<ExecuteReader name="BehaviorParameter">
<param name="behavior">
An instance of <see cref="T:System.Data.CommandBehavior" />, specifying options for batch execution and data retrieval.
Only <see cref="F:System.Data.CommandBehavior.SequentialAccess"/> and <see cref="F:System.Data.CommandBehavior.CloseConnection"/> are supported at the batch level.
</param>
<summary>
Sends the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Commands" /> to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Connection" /> and builds a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" />.
</summary>
<example>
<para>
The following example creates a <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> and a <see cref="T:Microsoft.Data.SqlClient.SqlBatch" />, then adds multiple <see cref="T:Microsoft.Data.SqlClient.SqlBatchCommand" /> objects to the batch. It then executes the batch, creating a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" />. The example reads through the results of the batch commands, writing them to the console. Finally, the example closes the <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" /> and then the <see cref="T:Microsoft.Data.SqlClient.SqlConnection" /> as the <c>using</c> blocks fall out of scope.
</para>
<code language="c#">
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 &lt; 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&lt;int&gt;(count);
using (SqlDataReader reader = batch.ExecuteReader(CommandBehavior.CloseConnection))
{
do
{
while (reader.Read())
{
results.Add(reader.GetFieldValue&lt;int&gt;(0));
}
} while (reader.NextResult());
}
Console.WriteLine(string.Join(", ", results));
}
}
</code>
</example>
</ExecuteReader>
<ExecuteReaderAsync name="NoParameter">
<param name="cancellationToken">A token to cancel the asynchronous operation.</param>
<summary>
An asynchronous version of <see cref="M:Microsoft.Data.SqlClient.SqlBatch.ExecuteReader" />, which sends the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Commands" /> to the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Connection" /> and builds a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" />.
Exceptions will be reported via the returned Task object.
</summary>
<returns>A task representing the asynchronous operation.</returns>
<exception cref="T:Microsoft.Data.SqlClient.SqlException">
An error occurred while executing the batch.
</exception>
<exception cref="T:System.ArgumentException">
The <see cref="T:System.Data.CommandBehavior" /> value is invalid.
</exception>
<exception cref="T:System.OperationCanceledException">
The cancellation token was canceled. This exception is stored into the returned task.
</exception>
</ExecuteReaderAsync>
<ExecuteReaderAsync name="BehaviorParameter">
<param name="behavior">
An instance of <see cref="T:System.Data.CommandBehavior" />, specifying options for batch execution and data retrieval.
Only <see cref="F:System.Data.CommandBehavior.SequentialAccess"/> and <see cref="F:System.Data.CommandBehavior.CloseConnection"/> are supported at the batch level.
</param>
<param name="cancellationToken">A token to cancel the asynchronous operation.</param>
<summary>
An asynchronous version of <see cref="M:Microsoft.Data.SqlClient.SqlBatch.ExecuteReader" />, which sends the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Commands" /> to the <see cref="P:Microsoft.Data.SqlClient.SqlBatch.Connection" /> and builds a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" />.
Expand Down Expand Up @@ -303,6 +381,7 @@
<ExecuteDbDataReader>
<param name="behavior">
An instance of <see cref="T:System.Data.CommandBehavior" />, specifying options for batch execution and data retrieval.
Only <see cref="F:System.Data.CommandBehavior.SequentialAccess"/> and <see cref="F:System.Data.CommandBehavior.CloseConnection"/> are supported at the batch level.
</param>
<summary>
Executes the batch against its connection, returning a <see cref="T:Microsoft.Data.SqlClient.SqlDataReader" /> which can be used to access the results.
Expand All @@ -326,7 +405,10 @@ When the batch returns multiple result sets from different commands, <xref:Micro
</exception>
</ExecuteDbDataReader>
<ExecuteDbDataReaderAsync>
<param name="behavior">One of the enumeration values that specifies options for batch execution and data retrieval.</param>
<param name="behavior">
An instance of <see cref="T:System.Data.CommandBehavior" />, specifying options for batch execution and data retrieval.
Only <see cref="F:System.Data.CommandBehavior.SequentialAccess"/> and <see cref="F:System.Data.CommandBehavior.CloseConnection"/> are supported at the batch level.
</param>
<param name="cancellationToken">A token to cancel the asynchronous operation.</param>
<summary>
This implementation invokes the <see cref="M:Microsoft.Data.SqlClient.SqlCommand.ExecuteReaderAsync" /> 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.
Expand Down
5 changes: 3 additions & 2 deletions doc/snippets/Microsoft.Data.SqlClient/SqlBatchCommand.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<docs>
<docs>
<members name="SqlBatchCommand">
<SqlBatchCommand>
<summary>
Expand Down Expand Up @@ -87,7 +87,8 @@
</Parameters>
<CommandBehavior>
<summary>
One of the <see cref="T:System.Data.CommandBehavior" /> values, indicating options for statement execution and data retrieval.
An instance of <see cref="T:System.Data.CommandBehavior" />, specifying options for statement execution and data retrieval.
Only <see cref="F:System.Data.CommandBehavior.SchemaOnly"/> and <see cref="F:System.Data.CommandBehavior.KeyInfo"/> are supported at the statement level.
</summary>
</CommandBehavior>
<ColumnEncryptionSetting>
Expand Down
16 changes: 14 additions & 2 deletions src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,26 @@ public class SqlBatch : System.IDisposable, System.IAsyncDisposable
#else
public System.Threading.Tasks.Task<int> ExecuteNonQueryAsync(System.Threading.CancellationToken cancellationToken = default) => throw null;
#endif
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader/*'/>
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader[@name="NoParameter"]/*'/>
public Microsoft.Data.SqlClient.SqlDataReader ExecuteReader() => throw null;
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync/*'/>
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader[@name="BehaviorParameter"]/*'/>
#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
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync[@name="NoParameter"]/*'/>
#if NET
public new System.Threading.Tasks.Task<Microsoft.Data.SqlClient.SqlDataReader> ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null;
#else
public System.Threading.Tasks.Task<Microsoft.Data.SqlClient.SqlDataReader> ExecuteReaderAsync(System.Threading.CancellationToken cancellationToken = default) => throw null;
#endif
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync[@name="BehaviorParameter"]/*'/>
#if NET
public new System.Threading.Tasks.Task<Microsoft.Data.SqlClient.SqlDataReader> ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null;
#else
public System.Threading.Tasks.Task<Microsoft.Data.SqlClient.SqlDataReader> ExecuteReaderAsync(System.Data.CommandBehavior behavior, System.Threading.CancellationToken cancellationToken = default) => throw null;
#endif
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteScalar/*'/>
#if NET
public override object ExecuteScalar() => throw null;
Expand Down
Comment thread
campersau marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -266,24 +266,54 @@ SqlTransaction Transaction
}
}

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader/*'/>
public SqlDataReader ExecuteReader()
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader[@name="NoParameter"]/*'/>
public SqlDataReader ExecuteReader() => ExecuteReader(CommandBehavior.Default);

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReader[@name="BehaviorParameter"]/*'/>
public
#if NET
new
#endif
SqlDataReader ExecuteReader(CommandBehavior behavior)
{
ValidateExecuteCommandBehavior(nameof(ExecuteReader), behavior);

CheckDisposed();
SetupBatchCommandExecute();
return _batchCommand.ExecuteReader();
return _batchCommand.ExecuteReader(behavior);
}

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync/*'/>
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync[@name="NoParameter"]/*'/>
public
#if NET
new
#endif
Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = default)
Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = default) => ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteReaderAsync[@name="BehaviorParameter"]/*'/>
public
#if NET
new
#endif
Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken = default)
{
ValidateExecuteCommandBehavior(nameof(ExecuteReaderAsync), behavior);

CheckDisposed();
SetupBatchCommandExecute();
return _batchCommand.ExecuteReaderAsync(cancellationToken);
return _batchCommand.ExecuteReaderAsync(behavior, cancellationToken)
.ContinueWith((result) =>
{
if (result.IsFaulted)
{
throw result.Exception.InnerException;
}
return result.Result;
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled,
TaskScheduler.Default
);
}

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteDbDataReader/*'/>
Expand All @@ -293,7 +323,7 @@ Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = def
#else
virtual
#endif
DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => ExecuteReader();
DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => ExecuteReader(behavior);

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlBatch.xml' path='docs/members[@name="SqlBatch"]/ExecuteDbDataReaderAsync/*'/>
protected
Expand All @@ -302,24 +332,7 @@ Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = def
#else
virtual
#endif
Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
CheckDisposed();
SetupBatchCommandExecute();
return _batchCommand.ExecuteReaderAsync(cancellationToken)
.ContinueWith<DbDataReader>((result) =>
{
if (result.IsFaulted)
{
throw result.Exception.InnerException;
}
return result.Result;
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled,
TaskScheduler.Default
);
}
async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) => await ExecuteReaderAsync(behavior, cancellationToken);

private void CheckDisposed()
{
Expand Down Expand Up @@ -350,5 +363,30 @@ private void SetupBatchCommandExecute()
}
_batchCommand.SetBatchRPCModeReadyToExecute();
}

/// <summary>
/// Validates that the provided <see cref="CommandBehavior"/> is compatible with <see cref="SqlBatch"/>.
/// </summary>
/// <param name="method">The name of the calling method for error reporting.</param>
/// <param name="behavior">The behavior flags to validate.</param>
/// <remarks>
/// <para>
/// Only <see cref="CommandBehavior.SequentialAccess"/> and <see cref="CommandBehavior.CloseConnection"/>
/// are supported at the batch level.
/// </para>
/// <para>
/// To apply other behaviors (such as <see cref="CommandBehavior.SingleRow"/> or <see cref="CommandBehavior.SchemaOnly"/>),
/// they must be set on individual <see cref="SqlBatchCommand"/> instances within the batch.
/// </para>
/// </remarks>
/// <exception cref="NotSupportedException">Thrown when unsupported behavior flags are detected.</exception>
internal static void ValidateExecuteCommandBehavior(string method, CommandBehavior behavior)
{
if (0 != (behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection)))
{
ADP.ValidateCommandBehavior(behavior);
throw ADP.NotSupportedCommandBehavior(behavior & ~(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection), method);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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, batchCommand.Parameters, ref rpc);
}

_RPCList.Add(rpc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -463,13 +462,13 @@ private void BuildExecuteSql(
SqlParameter sqlParam;

// @batch_text
commandText ??= GetCommandText(behavior);
string text = GetCommandText(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
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading