Injected Hosted services start with the app and stops with it. Framework doesn't provide you manual control. There should be underlying reasons for that like. Of course, you can implement your own solutions with threads but if your wish to go on with Microsoft's built-in solutions you can use Queued Background Task. You will start a background service with an empty task queue. Service will be listening to your first task to be added to the queue. You can add a task whenever you wish then it will be running at background.
It depends your implementation, did you write backgroundService start call in your startup? Personally I always write background service as a service never host them in IIS.
Cause don't know how is your design but this is expecting behavior of IIS. When you start the IIS it's actually starts to listen that port for http request and call your application's startup function. After the first http request coming through that site it's sending it to the application with new thread (this T time where your application truly run http flow).
Also background workers are not good for IIS, this answer explaining clearly.
Code snip will be more helpfull.
You should still be able to follow a similar format as the linked answer.
Mock the dependencies and inject them, invoke the methods under test and assert the expected behavior.
The following uses Moq to mock the dependencies along with to do the heavy lifting of injecting the dependencies.ServiceCollection
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestMethod]
public async Task DeviceToCloudMessageHostedService_Should_DoStuff() {
//Arrange
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IHostedService, DeviceToCloudMessageHostedService>();
//mock the dependencies for injection
services.AddSingleton(Mock.Of<IDeviceToCloudMessageService>(_ =>
_.DoStuff(It.IsAny<CancellationToken>()) == Task.CompletedTask
));
services.AddSingleton(Mock.Of<IOptionsMonitor<AppConfig>>(_ =>
_.CurrentValue == Mock.Of<AppConfig>(c =>
c.Parameter1 == TimeSpan.FromMilliseconds(1000)
)
));
var serviceProvider = services.BuildServiceProvider();
var hostedService = serviceProvider.GetService<IHostedService>();
//Act
await hostedService.StartAsync(CancellationToken.None);
await Task.Delay(1000);//Give some time to invoke the methods under test
await hostedService.StopAsync(CancellationToken.None);
//Assert
var deviceToCloudMessageService = serviceProvider
.GetRequiredService<IDeviceToCloudMessageService>();
//extracting mock to do verifications
var mock = Mock.Get(deviceToCloudMessageService);
//assert expected behavior
mock.Verify(_ => _.DoStuff(It.IsAny<CancellationToken>()), Times.AtLeastOnce);
mock.Verify(_ => _.EndStuff(), Times.AtLeastOnce());
}
Now, ideally this would count as testing framework code since you are basically testing that a behaves as expected when run, but it should demonstrate enough about how one would test such a service in isolationBackgroundService
I asked myself a similar question and made some search but couldn't find a good answer.
I solved the issue running every background service in with a cancellation token from Task.Run
I have 2 services like you.BackgroundService.ExecuteAsync()
public class BackgroundService1: BackgroundService
{
public BackgroundService1()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
//Second service is just like the first one:
public class BackgroundService2: BackgroundService
{
public BackgroundService2()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
and register them in Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<BackgroundService1>();
services.AddHostedService<BackgroundService2>();
})
.UseWindowsService()
Ok, i found the problem. Starting the Windows Server with Debugger.Launch() and enabling all Exceptions in Visual Studio finally led to an exception message:
Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'.
So yes, it has something to do with the environment - thank you @jdweng! The Windows Service does not seem to be able to get to the web development default certificate, so i will use an own dedicated certificate for my service.
It is a good idea to wrap the main method in a try catch block with an output to the Event Log to see such errors like so:
public static int Main(string[] args)
{
try
{
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception e)
{
var loggerFactory = LoggerFactory.Create(builder =>
{
SerilogLoggingBuilderExtensions.AddSerilog(
builder.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("MyNamespace", LogLevel.Debug)
.AddEventLog()
.AddConsole());
});
ILogger logger = loggerFactory.CreateLogger(typeof(Program));
logger.LogError(e.Message);
return -1;
}
}