Skip to content

Commit 5ccdc28

Browse files
Adds support for async service scope in request executor (#7826)
Co-authored-by: Michael Staib <michael@chillicream.com>
1 parent 2f8d695 commit 5ccdc28

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

src/HotChocolate/Core/src/Abstractions/Execution/VariableBatchRequest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,25 @@ public VariableBatchRequest(
112112
/// GraphQL request flags allow to limit the GraphQL executor capabilities.
113113
/// </summary>
114114
public GraphQLRequestFlags Flags { get; }
115+
116+
/// <summary>
117+
/// Creates a new request with the specified services.
118+
/// </summary>
119+
/// <param name="services">
120+
/// The services that shall be used while executing the operation.
121+
/// </param>
122+
/// <returns>
123+
/// Returns a new request with the specified services.
124+
/// </returns>
125+
public VariableBatchRequest WithServices(IServiceProvider services) =>
126+
new(
127+
Document,
128+
DocumentId,
129+
DocumentHash,
130+
OperationName,
131+
VariableValues,
132+
Extensions,
133+
ContextData,
134+
services,
135+
Flags);
115136
}

src/HotChocolate/Core/src/Execution/RequestExecutor.cs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ internal async Task<IExecutionResult> ExecuteAsync(
9999

100100
if (scopeDataLoader)
101101
{
102-
// we ensure that at the begin of each execution there is a fresh batching scope.
102+
// we ensure that at the beginning of each execution there is a fresh batching scope.
103103
services.InitializeDataLoaderScope();
104104
}
105105

@@ -153,7 +153,14 @@ internal async Task<IExecutionResult> ExecuteAsync(
153153
_contextPool.Return(context);
154154
}
155155

156-
scope?.Dispose();
156+
if(scope is IAsyncDisposable asyncScope)
157+
{
158+
await asyncScope.DisposeAsync();
159+
}
160+
else
161+
{
162+
scope?.Dispose();
163+
}
157164
}
158165
}
159166

@@ -174,7 +181,7 @@ public Task<IResponseStream> ExecuteBatchAsync(
174181

175182
private async IAsyncEnumerable<IOperationResult> CreateResponseStream(
176183
OperationRequestBatch requestBatch,
177-
[EnumeratorCancellation] CancellationToken cancellationToken = default)
184+
[EnumeratorCancellation] CancellationToken ct = default)
178185
{
179186
IServiceScope? scope = null;
180187

@@ -197,6 +204,31 @@ private async IAsyncEnumerable<IOperationResult> CreateResponseStream(
197204
// we ensure that at the start of each execution there is a fresh batching scope.
198205
services.InitializeDataLoaderScope();
199206

207+
try
208+
{
209+
await foreach (var result in ExecuteBatchStream(requestBatch, services, ct).ConfigureAwait(false))
210+
{
211+
yield return result;
212+
}
213+
}
214+
finally
215+
{
216+
if(scope is IAsyncDisposable asyncScope)
217+
{
218+
await asyncScope.DisposeAsync();
219+
}
220+
else
221+
{
222+
scope?.Dispose();
223+
}
224+
}
225+
}
226+
227+
private async IAsyncEnumerable<IOperationResult> ExecuteBatchStream(
228+
OperationRequestBatch requestBatch,
229+
IServiceProvider services,
230+
[EnumeratorCancellation] CancellationToken ct = default)
231+
{
200232
var requests = requestBatch.Requests;
201233
var requestCount = requests.Count;
202234
var tasks = new List<Task>(requestCount);
@@ -205,7 +237,7 @@ private async IAsyncEnumerable<IOperationResult> CreateResponseStream(
205237

206238
for (var i = 0; i < requestCount; i++)
207239
{
208-
tasks.Add(ExecuteBatchItemAsync(requests[i], i, completed, cancellationToken));
240+
tasks.Add(ExecuteBatchItemAsync(WithServices(requests[i], services), i, completed, ct));
209241
}
210242

211243
var buffer = new IOperationResult[8];
@@ -228,7 +260,7 @@ private async IAsyncEnumerable<IOperationResult> CreateResponseStream(
228260

229261
if (task.Status is not TaskStatus.RanToCompletion)
230262
{
231-
// we await to throw if its not successful.
263+
// we await to throw if it's not successful.
232264
await task;
233265
}
234266

@@ -252,6 +284,21 @@ private async IAsyncEnumerable<IOperationResult> CreateResponseStream(
252284
while (tasks.Count > 0 || bufferCount > 0);
253285
}
254286

287+
private static IOperationRequest WithServices(IOperationRequest request, IServiceProvider services)
288+
{
289+
switch (request)
290+
{
291+
case OperationRequest operationRequest:
292+
return operationRequest.WithServices(services);
293+
294+
case VariableBatchRequest variableBatchRequest:
295+
return variableBatchRequest.WithServices(services);
296+
297+
default:
298+
throw new InvalidOperationException("Unexpected request type.");
299+
}
300+
}
301+
255302
private async Task ExecuteBatchItemAsync(
256303
IOperationRequest request,
257304
int requestIndex,

0 commit comments

Comments
 (0)