Skip to content

Commit 2f080c6

Browse files
authored
Clarify and update scoped service DI content (#43363)
* Edit pass. Fixes #43263 * Fix highlighting
1 parent 07ec5b0 commit 2f080c6

File tree

3 files changed

+26
-40
lines changed

3 files changed

+26
-40
lines changed

docs/core/extensions/scoped-service.md

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ title: Use scoped services within a BackgroundService
33
description: Learn how to use scoped services within a BackgroundService in .NET.
44
author: IEvangelist
55
ms.author: dapine
6-
ms.date: 12/13/2023
6+
ms.date: 11/06/2024
77
ms.topic: tutorial
88
---
99

1010
# Use scoped services within a `BackgroundService`
1111

12-
When you register implementations of <xref:Microsoft.Extensions.Hosting.IHostedService> using any of the <xref:Microsoft.Extensions.DependencyInjection.ServiceCollectionHostedServiceExtensions.AddHostedService%2A> extension methods - the service is registered as a singleton. There may be scenarios where you'd like to rely on a scoped service. For more information, see [Dependency injection in .NET: Service lifetimes](dependency-injection.md#service-lifetimes).
12+
When you register implementations of <xref:Microsoft.Extensions.Hosting.IHostedService> using any of the <xref:Microsoft.Extensions.DependencyInjection.ServiceCollectionHostedServiceExtensions.AddHostedService%2A> extension methodsthe service is registered as a singleton. There might be scenarios where you'd like to rely on a scoped service. For more information, see [Dependency injection in .NET: Service lifetimes](dependency-injection.md#service-lifetimes).
1313

1414
In this tutorial, you learn how to:
1515

@@ -32,60 +32,55 @@ In this tutorial, you learn how to:
3232

3333
## Create scoped services
3434

35-
To use [scoped services](dependency-injection.md#scoped) within a `BackgroundService`, create a scope. No scope is created for a hosted service by default. The scoped background service contains the background task's logic.
35+
To use [scoped services](dependency-injection.md#scoped) within a `BackgroundService`, create a scope with the <xref:Microsoft.Extensions.DependencyInjection.IServiceScopeFactory.CreateScope?displayProperty=nameWithType> API. No scope is created for a hosted service by default. The scoped background service contains the background task's logic.
3636

3737
:::code source="snippets/workers/scoped-service/IScopedProcessingService.cs":::
3838

39-
The preceding interface defines a single `DoWorkAsync` method. To define the default implementation:
40-
41-
- The service is asynchronous. The `DoWorkAsync` method returns a `Task`. For demonstration purposes, a delay of ten seconds is awaited in the `DoWorkAsync` method.
42-
- An <xref:Microsoft.Extensions.Logging.ILogger> is injected into the service.:
39+
The preceding interface defines a single `DoWorkAsync` method. Create an implementation in a new class named *DefaultScopedProcessingService.cs*:
4340

4441
:::code source="snippets/workers/scoped-service/DefaultScopedProcessingService.cs":::
4542

46-
The hosted service creates a scope to resolve the scoped background service to call its `DoWorkAsync` method. `DoWorkAsync` returns a `Task`, which is awaited in `ExecuteAsync`:
43+
- An <xref:Microsoft.Extensions.Logging.ILogger> is injected into the service using a primary constructor.
44+
- The `DoWorkAsync` method returns a `Task` and accepts the <xref:System.Threading.CancellationToken>.
45+
- The method logs the instance identifier—the `_instanceId` is assigned whenever the class is instantiated.
4746

4847
## Rewrite the Worker class
4948

5049
Replace the existing `Worker` class with the following C# code, and rename the file to *ScopedBackgroundService.cs*:
5150

52-
:::code source="snippets/workers/scoped-service/ScopedBackgroundService.cs" highlight="22-28":::
51+
:::code source="snippets/workers/scoped-service/ScopedBackgroundService.cs" highlight="14-24":::
5352

54-
In the preceding code, an explicit scope is created and the `IScopedProcessingService` implementation is resolved from the dependency injection service scope factory. The resolved service instance is scoped, and its `DoWorkAsync` method is awaited.
53+
In the preceding code, while the `stoppingToken` isn't canceled, the `IServiceScopeFactory` is used to create a scope. From the `IServiceScope`, the `IScopedProcessingService` is resolved. The `DoWorkAsync` method is awaited, and the `stoppingToken` is passed to the method. Finally, the execution is delayed for 10 seconds and the loop continues. Each time the `DoWorkAsync` method is called, a new instance of the `DefaultScopedProcessingService` is created and the instance identifier is logged.
5554

5655
Replace the template *Program.cs* file contents with the following C# code:
5756

5857
:::code source="snippets/workers/scoped-service/Program.cs" highlight="4-5":::
5958

60-
The services are registered in (*Program.cs*). The hosted service is registered with the `AddHostedService` extension method.
59+
The services are registered in (*Program.cs*). The hosted service is registered with the <xref:Microsoft.Extensions.DependencyInjection.ServiceCollectionHostedServiceExtensions.AddHostedService*> extension method.
6160

6261
For more information on registering services, see [Dependency injection in .NET](dependency-injection.md).
6362

6463
## Verify service functionality
6564

6665
[!INCLUDE [run-app](includes/run-app.md)]
6766

68-
Let the application run for a bit to generate several execution count increments. You will see output similar to the following:
67+
Let the application run for a bit to generate several calls to `DoWorkAsync`, thus logging new instance identifiers. You see output similar to the following logs:
6968

7069
```Output
7170
info: App.ScopedService.ScopedBackgroundService[0]
7271
ScopedBackgroundService is running.
73-
info: App.ScopedService.ScopedBackgroundService[0]
74-
ScopedBackgroundService is working.
7572
info: App.ScopedService.DefaultScopedProcessingService[0]
76-
DefaultScopedProcessingService working, execution count: 1
73+
DefaultScopedProcessingService doing work, instance ID: 8986a86f-b444-4139-b9ea-587daae4a6dd
7774
info: Microsoft.Hosting.Lifetime[0]
7875
Application started. Press Ctrl+C to shut down.
7976
info: Microsoft.Hosting.Lifetime[0]
8077
Hosting environment: Development
8178
info: Microsoft.Hosting.Lifetime[0]
8279
Content root path: .\scoped-service
8380
info: App.ScopedService.DefaultScopedProcessingService[0]
84-
DefaultScopedProcessingService working, execution count: 2
85-
info: App.ScopedService.DefaultScopedProcessingService[0]
86-
DefaultScopedProcessingService working, execution count: 3
81+
DefaultScopedProcessingService doing work, instance ID: 07a4a760-8e5a-4c0a-9e73-fcb2f93157d3
8782
info: App.ScopedService.DefaultScopedProcessingService[0]
88-
DefaultScopedProcessingService working, execution count: 4
83+
DefaultScopedProcessingService doing work, instance ID: c847f432-acca-47ee-8720-1030859ce354
8984
info: Microsoft.Hosting.Lifetime[0]
9085
Application is shutting down...
9186
info: App.ScopedService.ScopedBackgroundService[0]

docs/core/extensions/snippets/workers/scoped-service/DefaultScopedProcessingService.cs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,15 @@
33
public sealed class DefaultScopedProcessingService(
44
ILogger<DefaultScopedProcessingService> logger) : IScopedProcessingService
55
{
6-
private int _executionCount;
6+
private readonly string _instanceId = Guid.NewGuid().ToString();
77

8-
public async Task DoWorkAsync(CancellationToken stoppingToken)
8+
public Task DoWorkAsync(CancellationToken stoppingToken)
99
{
10-
while (!stoppingToken.IsCancellationRequested)
11-
{
12-
++ _executionCount;
10+
logger.LogInformation(
11+
"{ServiceName} doing work, instance ID: {Id}",
12+
nameof(DefaultScopedProcessingService),
13+
_instanceId);
1314

14-
logger.LogInformation(
15-
"{ServiceName} working, execution count: {Count}",
16-
nameof(DefaultScopedProcessingService),
17-
_executionCount);
18-
19-
await Task.Delay(10_000, stoppingToken);
20-
}
15+
return Task.CompletedTask;
2116
}
2217
}

docs/core/extensions/snippets/workers/scoped-service/ScopedBackgroundService.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
1111
logger.LogInformation(
1212
"{Name} is running.", ClassName);
1313

14-
await DoWorkAsync(stoppingToken);
15-
}
16-
17-
private async Task DoWorkAsync(CancellationToken stoppingToken)
18-
{
19-
logger.LogInformation(
20-
"{Name} is working.", ClassName);
21-
22-
using (IServiceScope scope = serviceScopeFactory.CreateScope())
14+
while (!stoppingToken.IsCancellationRequested)
2315
{
16+
using IServiceScope scope = serviceScopeFactory.CreateScope();
17+
2418
IScopedProcessingService scopedProcessingService =
2519
scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
2620

2721
await scopedProcessingService.DoWorkAsync(stoppingToken);
22+
23+
await Task.Delay(10_000, stoppingToken);
2824
}
2925
}
3026

0 commit comments

Comments
 (0)