C
C#2y ago
Hejle

.NET Generic Host Writing "Application Started" when its done with its work

So I have been playing around with the Generic Host (Microsoft.Hosting.Extensions and HostedServices. So I have this output, where "Application started" comes after my service have stopped working. I am not sure about how, I should structure the code to get the "Application Started" before my service runs. I am currently using StartAsync, but gets the same behaviour with RunAsync StartAsync can be seen here: https://source.dot.net/#Microsoft.Extensions.Hosting/Internal/Host.cs,57 My Output
info: MyService[0]
Service started
info: MyService[0]
Service done
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\Users\JOAH\source\repos\GenericHost\GenericHost\bin\Debug\net7.0
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
info: MyService[0]
Service stopped
info: MyService[0]
Service started
info: MyService[0]
Service done
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\Users\JOAH\source\repos\GenericHost\GenericHost\bin\Debug\net7.0
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
info: MyService[0]
Service stopped
My Code
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = Host.CreateDefaultBuilder()
.UseConsoleLifetime()
.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
}).ConfigureLogging(loggingBuilder => loggingBuilder.AddConsole())
.Build();
var cts = new CancellationTokenSource();

await host.StartAsync(cts.Token);
await host.StopAsync();

class MyService : IHostedService
{
private readonly ILogger<MyService> _logger;

public MyService(ILogger<MyService> logger)
{
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Service started");
await Task.Delay(1000); //Important work, probably
_logger.LogInformation("Service done");
return;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Service stopped");
return Task.CompletedTask;
}
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = Host.CreateDefaultBuilder()
.UseConsoleLifetime()
.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
}).ConfigureLogging(loggingBuilder => loggingBuilder.AddConsole())
.Build();
var cts = new CancellationTokenSource();

await host.StartAsync(cts.Token);
await host.StopAsync();

class MyService : IHostedService
{
private readonly ILogger<MyService> _logger;

public MyService(ILogger<MyService> logger)
{
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Service started");
await Task.Delay(1000); //Important work, probably
_logger.LogInformation("Service done");
return;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Service stopped");
return Task.CompletedTask;
}
}
15 Replies
Omnissiah
Omnissiah2y ago
I should structure the code to get the "Application Started" before my service runs
why would you want that?
Hejle
HejleOP2y ago
Because currently my services prints that it starts, just as it completes
Omnissiah
Omnissiah2y ago
well it does nothing 😐 apart from waiting 1 sec so again why do you care that your service is started before or after "Application Started"?
Hejle
HejleOP2y ago
The Thread.Sleep could be swapped out with some real work, like fetching data from an API or something
Omnissiah
Omnissiah2y ago
ps it shouldn't be Thread.Sleep but await Task.Delay why do you care when this happens are there dependencies?
Hejle
HejleOP2y ago
I am mainly interested how it works, as I could get it working without using a HostedService or even the GenericHost. So I mainly care, because I think the startup messages (printing how to quick, what enviroment its running in and rootpath is good information) While I could make the startup-message myself, it would feel stupid, to have: Startup-message "Work-being-done"-messages Startup-message Closing-message
Omnissiah
Omnissiah2y ago
i'm sorry i really don't get what's the issue (also because you didn't finish you phrase up there) StartAsync is called when the application starts you can log stuff in there if you want to what more do you want like are you just saying that you want your logs after the startup messages
Pobiega
Pobiega2y ago
hosted services traditionally dont do imperative work once then finish, which is what the code you have shown above does the whole idea of a service is that its a long running thing that has a distinct start and stop phase, normally starting a server or process that does something, perhaps on a timer. so the reason you are getting "unsual" behaviour is that you are essentially missusing the IHostedService structure
Hejle
HejleOP2y ago
Which structure would/should I use instead?
Pobiega
Pobiega2y ago
well if you just want a program that runs its code once then terminates, you dont need generic host just a normal console app. You could use the hostbuilder to get config/DI setup for you, but then dont start the host, just resolve the actual application class you make instead remember, generic host is for hosting long living processes not running a one-and-done process
Hejle
HejleOP2y ago
Yeah, is using it for config/DI
Pobiega
Pobiega2y ago
I personally roll my own configbuilder + DI setup for simple console apps, but I can see the appeal in having it "done for you"
public class Program
{
public static async Task Main()
{
var builder = new HostApplicationBuilder();
builder.Services.AddTransient<MyActualApplicationClass>();

var host = builder.Build();
var runner = host.Services.GetRequiredService<MyActualApplicationClass>();

await runner.RunAsync();
}
}

public class MyActualApplicationClass
{
public async Task RunAsync()
{
throw new NotImplementedException();
}
}
public class Program
{
public static async Task Main()
{
var builder = new HostApplicationBuilder();
builder.Services.AddTransient<MyActualApplicationClass>();

var host = builder.Build();
var runner = host.Services.GetRequiredService<MyActualApplicationClass>();

await runner.RunAsync();
}
}

public class MyActualApplicationClass
{
public async Task RunAsync()
{
throw new NotImplementedException();
}
}
something like this
Hejle
HejleOP2y ago
Thanks for the help
Omnissiah
Omnissiah2y ago
or after having configured services get an ILogger and log what you need but application startup class would be better
Pobiega
Pobiega2y ago
yeah you can inject whatever you want into MyActualApplicationClass here a logger, a config object, whatevs
Want results from more Discord servers?
Add your server