Skip to content

Commit 37fd367

Browse files
IEvangelistCopilot
andauthored
Short-lived hosted scenario. (#46460)
* Fixes #45902 * Did I not save the code? * Update docs/core/extensions/workers.md Co-authored-by: Copilot <[email protected]> * Move and correct code ref --------- Co-authored-by: Copilot <[email protected]>
1 parent dcc8d06 commit 37fd367

File tree

4 files changed

+77
-4
lines changed

4 files changed

+77
-4
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
internal sealed class JobRunner(ILogger<JobRunner> logger)
4+
{
5+
public async Task RunAsync()
6+
{
7+
logger.LogInformation("Starting job...");
8+
9+
// Simulate work
10+
await Task.Delay(1000);
11+
12+
// Simulate failure
13+
// throw new InvalidOperationException("Something went wrong!");
14+
15+
logger.LogInformation("Job completed successfully.");
16+
}
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Hosting;
3+
using Microsoft.Extensions.Logging;
4+
5+
var builder = Host.CreateApplicationBuilder(args);
6+
builder.Services.AddSingleton<JobRunner>();
7+
8+
using var host = builder.Build();
9+
10+
try
11+
{
12+
var runner = host.Services.GetRequiredService<JobRunner>();
13+
14+
await runner.RunAsync();
15+
16+
return 0; // success
17+
}
18+
catch (Exception ex)
19+
{
20+
var logger = host.Services.GetRequiredService<ILogger<Program>>();
21+
22+
logger.LogError(ex, "Unhandled exception occurred during job execution.");
23+
24+
return 1; // failure
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Worker">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>true</ImplicitUsings>
7+
<RootNamespace>ShortLived.App</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
12+
</ItemGroup>
13+
</Project>

docs/core/extensions/workers.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Worker Services
33
description: Learn how to implement a custom IHostedService and use existing implementations in C#. Discover various worker implementations, templates, and service patterns.
44
author: IEvangelist
55
ms.author: dapine
6-
ms.date: 12/13/2023
6+
ms.date: 05/28/2025
77
ms.topic: overview
88
---
99

@@ -136,21 +136,39 @@ These two methods serve as *lifecycle* methods - they're called during host star
136136
137137
## Signal completion
138138

139-
In most common scenarios, you don't need to explicitly signal the completion of a hosted service. When the host starts the services, they're designed to run until the host is stopped. In some scenarios, however, you may need to signal the completion of the entire host application when the service completes. To signal the completion, consider the following `Worker` class:
139+
In most common scenarios, you don't need to explicitly signal the completion of a hosted service. When the host starts the services, they're designed to run until the host is stopped. In some scenarios, however, you might need to signal the completion of the entire host application when the service completes. To signal the completion, consider the following `Worker` class:
140140

141141
:::code source="snippets/workers/signal-completion-service/App.SignalCompletionService/Worker.cs":::
142142

143-
In the preceding code, the `ExecuteAsync` method doesn't loop, and when it's complete it calls <xref:Microsoft.Extensions.Hosting.IHostApplicationLifetime.StopApplication?displayProperty=nameWithType>.
143+
In the preceding code, the <xref:Microsoft.Extensions.Hosting.BackgroundService.ExecuteAsync(System.Threading.CancellationToken)?displayProperty=nameWithType> method doesn't loop, and when it's complete it calls <xref:Microsoft.Extensions.Hosting.IHostApplicationLifetime.StopApplication?displayProperty=nameWithType>.
144144

145145
> [!IMPORTANT]
146-
> This will signal to the host that it should stop, and without this call to `StopApplication` the host will continue to run indefinitely.
146+
> This will signal to the host that it should stop, and without this call to `StopApplication` the host will continue to run indefinitely. If you intend to run a short-lived hosted service (run once scenario), and you want to use the Worker template, you must call `StopApplication` to signal the host to stop.
147147
148148
For more information, see:
149149

150150
- [.NET Generic Host: IHostApplicationLifetime](generic-host.md#ihostapplicationlifetime)
151151
- [.NET Generic Host: Host shutdown](generic-host.md#host-shutdown)
152152
- [.NET Generic Host: Hosting shutdown process](generic-host.md#hosting-shutdown-process)
153153

154+
### Alternative approach
155+
156+
For a short-lived app that needs dependency injection, logging, and configuration, use the [.NET Generic Host](generic-host.md) instead of the Worker template. This lets you use these features without the `Worker` class. A simple example of a short-lived app using the generic host might define a project file like the following:
157+
158+
:::code language="xml" source="snippets/hosts/ShortLived.App/ShortLived.App.csproj":::
159+
160+
It's `Program` class might look something like the following:
161+
162+
:::code language="csharp" source="snippets/hosts/ShortLived.App/Program.cs":::
163+
164+
The preceding code creates a `JobRunner` service, which is a custom class that contains the logic for the job to run. The `RunAsync` method is called on the `JobRunner`, and if it completes successfully, the app returns `0`. If an unhandled exception occurs, it logs the error and returns `1`.
165+
166+
In this simple scenario, the `JobRunner` class could look like this:
167+
168+
:::code language="csharp" source="snippets/hosts/ShortLived.App/JobRunner.cs":::
169+
170+
You'd obviously need to add real logic to the `RunAsync` method, but this example demonstrates how to use the generic host for a short-lived app without the need for a `Worker` class, and without the need for explicitly signaling the completion of the host.
171+
154172
## See also
155173

156174
- <xref:Microsoft.Extensions.Hosting.BackgroundService> subclass tutorials:

0 commit comments

Comments
 (0)