if (_options.ServicesStartConcurrently)
{
- Task tasks = Task.WhenAll(_hostedServices.Select(async service =>
+ List<Task> tasks = new List<Task>();
+
+ foreach (IHostedService hostedService in _hostedServices)
{
- await service.StartAsync(combinedCancellationToken).ConfigureAwait(false);
+ tasks.Add(Task.Run(() => StartAndTryToExecuteAsync(hostedService, combinedCancellationToken), combinedCancellationToken));
+ }
- if (service is BackgroundService backgroundService)
- {
- _ = TryExecuteBackgroundServiceAsync(backgroundService);
- }
- }));
+ Task groupedTasks = Task.WhenAll(tasks);
try
{
- await tasks.ConfigureAwait(false);
+ await groupedTasks.ConfigureAwait(false);
}
catch (Exception ex)
{
- exceptions.AddRange(tasks.Exception?.InnerExceptions ?? new[] { ex }.AsEnumerable());
+ exceptions.AddRange(groupedTasks.Exception?.InnerExceptions ?? new[] { ex }.AsEnumerable());
}
}
else
try
{
// Fire IHostedService.Start
- await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
-
- if (hostedService is BackgroundService backgroundService)
- {
- _ = TryExecuteBackgroundServiceAsync(backgroundService);
- }
+ await StartAndTryToExecuteAsync(hostedService, combinedCancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Started();
}
+ private async Task StartAndTryToExecuteAsync(IHostedService service, CancellationToken combinedCancellationToken)
+ {
+ await service.StartAsync(combinedCancellationToken).ConfigureAwait(false);
+
+ if (service is BackgroundService backgroundService)
+ {
+ _ = TryExecuteBackgroundServiceAsync(backgroundService);
+ }
+ }
+
private async Task TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)
{
// backgroundService.ExecuteTask may not be set (e.g. if the derived class doesn't call base.StartAsync)
if (_options.ServicesStopConcurrently)
{
- Task tasks = Task.WhenAll(hostedServices.Select(async service => await service.StopAsync(token).ConfigureAwait(false)));
+ List<Task> tasks = new List<Task>();
+
+ foreach (IHostedService hostedService in hostedServices)
+ {
+ tasks.Add(Task.Run(() => hostedService.StopAsync(token), token));
+ }
+
+ Task groupedTasks = Task.WhenAll(tasks);
try
{
- await tasks.ConfigureAwait(false);
+ await groupedTasks.ConfigureAwait(false);
}
catch (Exception ex)
{
- exceptions.AddRange(tasks.Exception?.InnerExceptions ?? new[] { ex }.AsEnumerable());
+ exceptions.AddRange(groupedTasks.Exception?.InnerExceptions ?? new[] { ex }.AsEnumerable());
}
}
else
namespace Microsoft.Extensions.Hosting.Unit.Tests;
-internal class DelegateHostedService : IHostedService, IDisposable
+internal class DelegateHostedService : IHostedService, IDisposable, IEquatable<DelegateHostedService>
{
private readonly Action _started;
private readonly Action _stopping;
_disposing = disposing;
}
+ public int? Identifier { get; set; }
+
public Task StartAsync(CancellationToken token)
{
StartDate = DateTimeOffset.Now;
public DateTimeOffset StartDate { get; private set; }
public DateTimeOffset StopDate { get; private set; }
+
+ public bool Equals(DelegateHostedService other) => this == other;
+
+ public override string ToString() => $"DelegateHostedService: Id={Identifier}, StartDate={StartDate}, StopDate={StopDate}";
}
{
var index = i;
var service = new DelegateHostedService(() => { events[index, 0] = true; }, () => { events[index, 1] = true; } , () => { });
-
+ service.Identifier = index;
hostedServices[index] = service;
}
Assert.False(events[i, 1]);
}
- // Ensures that IHostedService instances are started in FIFO order
- AssertExtensions.CollectionEqual(hostedServices, hostedServices.OrderBy(h => h.StartDate), EqualityComparer<DelegateHostedService>.Default);
+ // Ensures that IHostedService instances are started in FIFO order when services are started non concurrently
+ if (hostedServiceCount > 0 && !startConcurrently)
+ {
+ AssertExtensions.Equal(hostedServices, hostedServices.OrderBy(h => h.StartDate).ToArray());
+ }
await host.StopAsync(CancellationToken.None);
Assert.True(events[i, 1]);
}
- // Ensures that IHostedService instances are stopped in LIFO order
- AssertExtensions.CollectionEqual(hostedServices.Reverse(), hostedServices.OrderBy(h => h.StopDate), EqualityComparer<DelegateHostedService>.Default);
+ // Ensures that IHostedService instances are stopped in LIFO order when services are stopped non concurrently
+ if (hostedServiceCount > 0 && !stopConcurrently)
+ {
+ AssertExtensions.Equal(hostedServices, hostedServices.OrderByDescending(h => h.StopDate).ToArray());
+ }
}
[Fact]