From abeae907c8bd59ccc3cd8accb4e2934355603bc4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Oct 2021 12:09:07 -0700 Subject: [PATCH] [release/6.0] Check BackgroundService.ExecuteTask for null (#60177) * Check BackgroundService.ExecuteTask for null In some scenarios, derived classes might not have called base.StartAsync, and ExecuteTask will still be null. Ensure we don't fail in those cases. Fix #60131 * PR feedback Co-authored-by: Eric Erhardt --- .../src/Internal/Host.cs | 11 +++++- .../tests/UnitTests/Internal/HostTests.cs | 39 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs index 4041133a0a9..2f73146e695 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 @@ namespace Microsoft.Extensions.Hosting.Internal 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 d07c2bc57ea..b3133dba3be 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 @@ namespace Microsoft.Extensions.Hosting.Internal } } + /// + /// 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 @@ namespace Microsoft.Extensions.Hosting.Internal } } } + + 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; + } } } -- 2.34.1