Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
aaecb8c
Add SqlCommand.Reader partial
benrr101 Jul 29, 2025
ca78936
Merge ExecuteReader
benrr101 Jul 29, 2025
6381283
Merge RunExecuteReaderWithRetry
benrr101 Jul 29, 2025
6e39ea1
Merge RunExecuteReader#1
benrr101 Jul 29, 2025
57c7ee3
Merge RunExecuteReader#2
benrr101 Aug 5, 2025
bbe110e
Refactor out RunExecuteReaderTdsSetupReconnectContinuation (to match …
benrr101 Jul 31, 2025
16ca4d9
Factor out RunExecuteReaderTdsSetupContinuation (to match netcore)
benrr101 Jul 31, 2025
4ffe25a
Merge RunExecuteReaderTds
benrr101 Jul 31, 2025
912bc28
Merge RunExecuteReaderTdsSetupReconnectContinuation
benrr101 Aug 1, 2025
a92378f
Merge RunExecuteReaderTdsSetupContinuation
benrr101 Aug 1, 2025
187ee9a
Merge RunExecuteReaderTdsWithTransparentParameterEncryption
benrr101 Aug 1, 2025
4981042
Merge GenerateEnclavePackage
benrr101 Aug 1, 2025
3a5d3bc
Merge FinishExecuteReader
benrr101 Aug 1, 2025
2e78c26
Merge BuildRPC
benrr101 Aug 4, 2025
0cfc252
Merge BuildExecute
benrr101 Aug 4, 2025
d816be3
Merge BuildExecuteSql
benrr101 Aug 4, 2025
3cfd99d
Merge BuildPrepExec
benrr101 Aug 4, 2025
47906b0
Merge BeginExecuteReader
benrr101 Aug 4, 2025
075dfc4
Merging BeginExecuteReaderInternal
benrr101 Aug 4, 2025
d029e00
Merge BeginExecuteReaderInternalReadStage
benrr101 Aug 4, 2025
d1b6e16
Merge EndExecuteReader
benrr101 Aug 4, 2025
a61fdd0
Merge EndExecuteReaderInternal
benrr101 Aug 4, 2025
936f0d1
Merge InternalEndExecuteReader
benrr101 Aug 4, 2025
44d98d7
Merge CompleteAsyncExecuteReader
benrr101 Aug 4, 2025
75d2ad8
Merge ExecuteReaderAsync
benrr101 Aug 5, 2025
446e44a
Merge InternalExecuteReaderWithRetryAsync
benrr101 Aug 5, 2025
1cdf11c
Merge InternalExecuteReaderAsync
benrr101 Aug 5, 2025
ef4b823
Merge CleanupExecuteReaderAsync
benrr101 Aug 5, 2025
2c32217
F goes after E
benrr101 Aug 5, 2025
533d10e
Merge EndExecuteReaderAsync
benrr101 Aug 5, 2025
e622555
Merge ExecuteDbDataReader
benrr101 Aug 5, 2025
61f7b4a
Merge ExecuteDbDataReaderAsync
benrr101 Aug 5, 2025
4cba491
Merge SetCachedCommandExecuteReaderAsyncContext
benrr101 Aug 5, 2025
6a89074
Merge ExecuteReaderAsyncCallContext
benrr101 Aug 5, 2025
c0ba212
Address feedback from @edwardneal
benrr101 Oct 1, 2025
776caec
Add `?` to _activeConnection references in trace logging - fixes the …
benrr101 Oct 1, 2025
bc76b9b
Merge branch 'main' into dev/russellben/merge/sqlcommand-nocer-reader
benrr101 Oct 2, 2025
cb11f43
Add back line from netfx
benrr101 Oct 2, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -857,88 +857,6 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b
);
}

private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteReaderAsync | API | Correlation | Object Id {0}, Behavior {1}, Activity Id {2}, Client Connection Id {3}, Command Text '{4}'", ObjectID, (int)behavior, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteReaderAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText);
Guid operationId = default(Guid);
if (!_parentOperationStarted)
{
operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
}

// connection can be used as state in RegisterForConnectionCloseNotification continuation
// to avoid an allocation so use it as the state value if possible but it can be changed if
// you need it for a more important piece of data that justifies the tuple allocation later
TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>(_activeConnection);

CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled)
{
if (cancellationToken.IsCancellationRequested)
{
source.SetCanceled();
return source.Task;
}
registration = cancellationToken.Register(s_cancelIgnoreFailure, this);
}

Task<SqlDataReader> returnedTask = source.Task;
ExecuteReaderAsyncCallContext context = null;
try
{
returnedTask = RegisterForConnectionCloseNotification(returnedTask);

if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection)
{
context = Interlocked.Exchange(ref sqlInternalConnection.CachedCommandExecuteReaderAsyncContext, null);
}
if (context is null)
{
context = new ExecuteReaderAsyncCallContext();
}
context.Set(this, source, registration, behavior, operationId);

Task<SqlDataReader>.Factory.FromAsync(
beginMethod: static (AsyncCallback callback, object stateObject) =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)stateObject;
return args.Command.BeginExecuteReaderInternal(args.CommandBehavior, callback, stateObject, args.Command.CommandTimeout, isRetry: false, asyncWrite: true);
},
endMethod: static (IAsyncResult asyncResult) =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)asyncResult.AsyncState;
return args.Command.EndExecuteReaderAsync(asyncResult);
},
state: context
).ContinueWith(
continuationAction: static (Task<SqlDataReader> task) =>
{
ExecuteReaderAsyncCallContext context = (ExecuteReaderAsyncCallContext)task.AsyncState;
SqlCommand command = context.Command;
Guid operationId = context.OperationID;
TaskCompletionSource<SqlDataReader> source = context.TaskCompletionSource;
context.Dispose();

command.CleanupExecuteReaderAsync(task, source, operationId);
},
scheduler: TaskScheduler.Default
);
}
catch (Exception e)
{
if (!_parentOperationStarted)
{
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
}

source.SetException(e);
context?.Dispose();
}

return returnedTask;
}

private void SetCachedCommandExecuteReaderAsyncContext(ExecuteReaderAsyncCallContext instance)
{
if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -820,80 +820,6 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b
);
}

private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteReaderAsync | API | Correlation | Object Id {0}, Behavior {1}, Activity Id {2}, Client Connection Id {3}, Command Text '{4}'", ObjectID, (int)behavior, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteReaderAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText);
SqlConnection.ExecutePermission.Demand();
Guid operationId = default(Guid);

// connection can be used as state in RegisterForConnectionCloseNotification continuation
// to avoid an allocation so use it as the state value if possible but it can be changed if
// you need it for a more important piece of data that justifies the tuple allocation later
TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>(_activeConnection);

CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled)
{
if (cancellationToken.IsCancellationRequested)
{
source.SetCanceled();
return source.Task;
}
registration = cancellationToken.Register(s_cancelIgnoreFailure, this);
}

Task<SqlDataReader> returnedTask = source.Task;
ExecuteReaderAsyncCallContext context = null;
try
{
returnedTask = RegisterForConnectionCloseNotification(returnedTask);

if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection)
{
context = Interlocked.Exchange(ref sqlInternalConnection.CachedCommandExecuteReaderAsyncContext, null);
}
if (context is null)
{
context = new ExecuteReaderAsyncCallContext();
}
context.Set(this, source, registration, behavior, operationId);

Task<SqlDataReader>.Factory.FromAsync(
beginMethod: static (AsyncCallback callback, object stateObject) =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)stateObject;
return args.Command.BeginExecuteReaderInternal(args.CommandBehavior, callback, stateObject, args.Command.CommandTimeout, isRetry: false, asyncWrite: true);
},
endMethod: static (IAsyncResult asyncResult) =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)asyncResult.AsyncState;
return args.Command.EndExecuteReaderAsync(asyncResult);
},
state: context
).ContinueWith(
continuationAction: static (Task<SqlDataReader> task) =>
{
ExecuteReaderAsyncCallContext context = (ExecuteReaderAsyncCallContext)task.AsyncState;
SqlCommand command = context.Command;
Guid operationId = context.OperationID;
TaskCompletionSource<SqlDataReader> source = context.TaskCompletionSource;
context.Dispose();

command.CleanupExecuteReaderAsync(task, source, operationId);
},
scheduler: TaskScheduler.Default
);
}
catch (Exception e)
{
source.SetException(e);
context?.Dispose();
}

return returnedTask;
}

private void SetCachedCommandExecuteReaderAsyncContext(ExecuteReaderAsyncCallContext instance)
{
if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,105 @@ private void GenerateEnclavePackage()
}
}

private Task<SqlDataReader> InternalExecuteReaderAsync(
CommandBehavior commandBehavior,
CancellationToken cancellationToken)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent(
"SqlCommand.InternalExecuteReaderAsync | API | Correlation | " +
$"Object Id {ObjectID}, " +
$"Behavior {(int)commandBehavior}, " +
$"Activity Id {ActivityCorrelator.Current}, " +
$"Client Connection Id {_activeConnection?.ClientConnectionId}, " +
$"Command Text '{CommandText}'");
SqlClientEventSource.Log.TryTraceEvent(
"SqlCommand.InternalExecuteReaderAsync | INFO | " +
$"Object Id {ObjectID}, " +
$"Client Connection Id {_activeConnection?.ClientConnectionId}, " +
$"Command Text '{CommandText}'");

Guid operationId = Guid.Empty;
#if NET
if (!_parentOperationStarted)
{
operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
}
#endif

// Connection can be used as state in RegisterForConnectionCloseNotification
// continuation to avoid an allocation so use it as the state value if possible, but it
// can be changed if you need it for a more important piece of data that justifies the
// tuple allocation later.
TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>(_activeConnection);

CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled)
{
if (cancellationToken.IsCancellationRequested)
{
source.SetCanceled();
return source.Task;
}

registration = cancellationToken.Register(s_cancelIgnoreFailure, state: this);
}

Task<SqlDataReader> returnedTask = source.Task;
ExecuteReaderAsyncCallContext context = null;
try
{
returnedTask = RegisterForConnectionCloseNotification(returnedTask);

if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection)
{
context = Interlocked.Exchange(
ref sqlInternalConnection.CachedCommandExecuteReaderAsyncContext,
null);
}

context ??= new ExecuteReaderAsyncCallContext();
context.Set(this, source, registration, commandBehavior, operationId);

Task<SqlDataReader>.Factory.FromAsync(
beginMethod: static (callback, state) =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)state;
return args.Command.BeginExecuteReaderInternal(
args.CommandBehavior,
callback,
state,
args.Command.CommandTimeout,
isRetry: false, // @TODO: Wait, this *is* a retry if the we're on the retry part of the reliability helper!
asyncWrite: true);
},
endMethod: static asyncResult =>
{
ExecuteReaderAsyncCallContext args = (ExecuteReaderAsyncCallContext)asyncResult.AsyncState;
return args.Command.EndExecuteReaderAsync(asyncResult);
},
state: context
).ContinueWith(
static task =>
{
ExecuteReaderAsyncCallContext context = (ExecuteReaderAsyncCallContext)task.AsyncState;
SqlCommand command = context.Command;
Guid operationId = context.OperationID;
TaskCompletionSource<SqlDataReader> source = context.TaskCompletionSource;

context.Dispose();
command.CleanupExecuteReaderAsync(task, source, operationId);
},
scheduler: TaskScheduler.Default);
}
catch (Exception e)
{
source.SetException(e);
context?.Dispose();
}

return returnedTask;
}

private Task<SqlDataReader> InternalExecuteReaderWithRetryAsync(
CommandBehavior commandBehavior,
CancellationToken cancellationToken)
Expand Down