diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs index 4041133a0a9f25..2f73146e695ef2 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs @@ -79,15 +79,22 @@ public async Task StartAsync(CancellationToken cancellationToken = default) private async Task TryExecuteBackgroundServiceAsync(BackgroundService backgroundService) { + // backgroundService.ExecuteTask may not be set (e.g. if the derived class doesn't call base.StartAsync) + Task backgroundTask = backgroundService.ExecuteTask; + if (backgroundTask == null) + { + return; + } + try { - await backgroundService.ExecuteTask.ConfigureAwait(false); + await backgroundTask.ConfigureAwait(false); } catch (Exception ex) { // When the host is being stopped, it cancels the background services. // This isn't an error condition, so don't log it as an error. - if (_stopCalled && backgroundService.ExecuteTask.IsCanceled && ex is OperationCanceledException) + if (_stopCalled && backgroundTask.IsCanceled && ex is OperationCanceledException) { return; } diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs index d07c2bc57eaa71..b3133dba3be886 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs @@ -1394,6 +1394,36 @@ public async Task HostNoErrorWhenServiceIsCanceledAsPartOfStop() } } + /// + /// Tests that when a BackgroundService does not call base, the Host still starts and stops successfully. + /// + [Fact] + public async Task StartOnBackgroundServiceThatDoesNotCallBase() + { + TestLoggerProvider logger = new TestLoggerProvider(); + + using IHost host = CreateBuilder() + .ConfigureLogging(logging => + { + logging.AddProvider(logger); + }) + .ConfigureServices(services => + { + services.AddHostedService(); + }) + .Build(); + + host.Start(); + await host.StopAsync(); + + foreach (LogEvent logEvent in logger.GetEvents()) + { + Assert.True(logEvent.LogLevel <= LogLevel.Information, "All logged events should be less than or equal to Information. No Warnings or Errors."); + + Assert.NotEqual("BackgroundServiceFaulted", logEvent.EventId.Name); + } + } + private IHostBuilder CreateBuilder(IConfiguration config = null) { return new HostBuilder().ConfigureHostConfiguration(builder => builder.AddConfiguration(config ?? new ConfigurationBuilder().Build())); @@ -1562,5 +1592,14 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } } + + private class BackgroundServiceDoesNotCallBase : BackgroundService + { + public override Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask; + + public override Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } } }