Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Sep 25, 2025

This PR adds support for hosted services (IHostedService) in WebAssemblyHost, enabling automatic startup and shutdown of background services in Blazor WebAssembly applications.

Description

Applications using WebAssemblyHost (e.g. Blazor WebAssembly clients) did not support hosted services out of the box. Services registered with AddHostedService were ignored when using WebAssemblyHostBuilder, unlike in applications built with WebApplicationBuilder or HostApplicationBuilder. This prevented libraries like OpenTelemetry from working properly in Blazor WebAssembly apps, as they rely on hosted services for initialization.

The implementation adds hosted service lifecycle management to WebAssemblyHost:

  • Service Discovery: Automatically discovers IHostedService implementations from the DI container
  • Startup Integration: Starts hosted services during RunAsyncCore() after component state restoration
  • Cleanup Integration: Stops hosted services during DisposeAsync() with proper error handling
  • HostedServiceExecutor: Encapsulates hosted service lifecycle logic in a dedicated executor class registered in DI, with integrated exception handling and logging using LoggerMessage source generator pattern
  • Clean Architecture: WebAssemblyHost simply resolves the executor from DI and calls it at the appropriate times, maintaining clean separation of concerns

Usage

Developers can now register hosted services in Blazor WebAssembly apps:

var builder = WebAssemblyHostBuilder.CreateDefault(args);

// Register hosted services - they'll start automatically
builder.Services.AddSingleton<IHostedService, MyBackgroundService>();

var host = builder.Build();
await host.RunAsync(); // Hosted services start here

Implementation Details

  • Added Microsoft.Extensions.Hosting.Abstractions reference to enable IHostedService support
  • Created HostedServiceExecutor class in the WebAssembly project to encapsulate hosted service lifecycle management
  • Registered HostedServiceExecutor as a singleton in DI in WebAssemblyHostBuilder.InitializeDefaultServices()
  • HostedServiceExecutor accepts ILogger<HostedServiceExecutor> and handles all exception aggregation and logging internally
  • Hosted services start after component state restoration (matching the timing suggested in the original issue)
  • Error handling ensures host disposal doesn't fail if hosted services throw exceptions during shutdown
  • Uses LoggerMessage source generator pattern for structured logging, following codebase conventions in WebAssemblyRenderer and WebAssemblyNavigationManager
  • WebAssemblyHost resolves the executor from DI and coordinates its invocation at the right times
  • Follows the same architectural pattern as the Hosting project for consistency across the codebase

Testing

Added comprehensive test coverage:

  • Service startup during host run
  • Service shutdown during host disposal
  • Error handling for faulty services
  • Integration with service registration patterns

All existing WebAssemblyHost functionality remains unchanged and all tests pass.

Original prompt

This section details on the original issue you should resolve

<issue_title>Support hosted services in WebAssemblyHost</issue_title>
<issue_description>### Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Applications using WebAssemblyHost (e.g. Blazor WebAssembly clients) do not support hosted services out of the box. Services registered with AddHostedService are ignored when using WebAssemblyHostBuilder, unlike in applications built with WebApplicationBuilder or HostApplicationBuilder.

Describe the solution you'd like

WebAssemblyHost should support automatically running hosted services registered with AddHostedService.

Additional context

This feature request has already been rejected in #33537. However, we might want to reconsider the decision. For example, support for hosted services might be beneficial for Open Telemetry integration in Blazor Wasm apps.</issue_description>

<agent_instructions>Look at

private readonly IEnumerable<IHostedService> _services;
and
private HostedServiceExecutor? _hostedServiceExecutor;

We want WebAssemblyHost to start the services here https://github.com/dotnet/aspnetcore/blob/main/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs#L139</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@pavelsavara We should measure the size/perf impact of adding it by default for WASM
Fixes #63637

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI changed the title [WIP] Support hosted services in WebAssemblyHost Support hosted services in WebAssemblyHost Sep 25, 2025
Copilot AI requested a review from javiercn September 25, 2025 10:12
@javiercn javiercn marked this pull request as ready for review December 1, 2025 13:18
@javiercn javiercn requested a review from a team as a code owner December 1, 2025 13:18
Copilot AI review requested due to automatic review settings December 1, 2025 13:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds hosted service (IHostedService) lifecycle management to WebAssemblyHost, enabling Blazor WebAssembly applications to use background services automatically. This addresses the limitation where services registered with AddHostedService were previously ignored in WebAssembly applications, and was specifically requested to support libraries like OpenTelemetry that rely on hosted services.

Key changes:

  • Hosted services are automatically started during RunAsyncCore() after component state restoration
  • Hosted services are stopped during DisposeAsync() with error aggregation and logging
  • Added dependency on Microsoft.Extensions.Hosting.Abstractions to enable IHostedService support

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj Adds reference to Microsoft.Extensions.Hosting.Abstractions package
src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs Implements hosted service lifecycle with start/stop methods and disposal integration
src/Components/WebAssembly/WebAssembly/test/Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj Adds reference to Microsoft.Extensions.Hosting.Abstractions for test project
src/Components/WebAssembly/WebAssembly/test/Hosting/WebAssemblyHostTest.cs Adds comprehensive tests for hosted service startup, shutdown, and error handling scenarios

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Comment on lines 95 to 96
var logger = Services.GetService<ILogger<WebAssemblyHost>>();
logger?.LogError(ex, "An error occurred stopping hosted services during disposal.");
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the LoggerMessage source generator pattern for structured logging. The codebase convention in WebAssemblyRenderer and WebAssemblyNavigationManager uses partial static Log classes with [LoggerMessage] attributes for better performance and consistency. Add a private static partial class Log with a LoggerMessage method for this error.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot do this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use LoggerMessage source generator pattern with a private static partial Log class. Changes in commit d8752fb.

}

// Throw an aggregate exception if there were any exceptions
if (exceptions is not null)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The condition check uses is not null which is correct according to nullable reference type conventions. However, the HostedServiceExecutor in src/Hosting/Hosting/src/Internal/HostedServiceExecutor.cs:43 uses != null for the same pattern. Consider using is not null consistently for better code alignment with C# null pattern matching practices, which this codebase already follows (see line 84, 105).

Copilot uses AI. Check for mistakes.
@javiercn javiercn added the area-blazor Includes: Blazor, Razor Components label Dec 3, 2025
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Dec 10, 2025
Copy link
Member

@maraf maraf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me 👍

Copilot AI requested a review from javiercn December 12, 2025 15:27
@javiercn javiercn removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Dec 12, 2025
@javiercn javiercn enabled auto-merge (squash) December 12, 2025 18:07
@javiercn javiercn merged commit 49a3054 into main Dec 15, 2025
26 checks passed
@javiercn javiercn deleted the copilot/fix-a520da0f-c4e7-445a-93de-e5985b1b7267 branch December 15, 2025 10:19
@dotnet-policy-service dotnet-policy-service bot added this to the 11.0-preview1 milestone Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components Attention: Shared Code Modified

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support hosted services in WebAssemblyHost

3 participants