Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

421
Views
The "correct" way to create a .NET Core console app without background services

I'm building a simple .NET Core console application that will read in basic options from the command line, then execute and terminate without user interaction. I'd like to take advantage of DI, so that lead me to using the .NET Core generic host.

All of the examples I've found that build a console app create a class that either implements IHostedService or extends BackgroundService. That class then gets added to the service container via AddHostedService and starts the application's work via StartAsync or ExecuteAsync. However, it seems that in all of these examples, they are implemementing a background service or some other application that runs in a loop or waits for requests until it gets shut down by the OS or receives some request to terminate. What if I just want an app that starts, does its thing, then exits? For example:

Program.cs:

namespace MyApp
{
    using System;
    using System.Threading.Tasks;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;

    public static class Program
    {
        public static async Task Main(string[] args)
        {
            await CreateHostBuilder(args).RunConsoleAsync();
        }

        private static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseConsoleLifetime()
                .ConfigureLogging(builder => builder.SetMinimumLevel(LogLevel.Warning))
                .ConfigureServices((hostContext, services) =>
                {
                    services.Configure<MyServiceOptions>(hostContext.Configuration);
                    services.AddHostedService<MyService>();
                    services.AddSingleton(Console.Out);
                });
    }
}

MyServiceOptions.cs:

namespace MyApp
{
    public class MyServiceOptions
    {
        public int OpCode { get; set; }
        public int Operand { get; set; }
    }
}

MyService.cs:

namespace MyApp
{
    using System.IO;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Options;

    public class MyService : IHostedService
    {
        private readonly MyServiceOptions _options;
        private readonly TextWriter _outputWriter;

        public MyService(TextWriter outputWriter, IOptions<MyServiceOptions> options)
        {
            _options = options.Value;
            _outputWriter = outputWriter;
        }

        public async Task StartAsync(CancellationToken cancellationToken)
        {
            _outputWriter.WriteLine("Starting work");

            DoOperation(_options.OpCode, _options.Operand);

            _outputWriter.WriteLine("Work complete");
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            _outputWriter.WriteLine("StopAsync");
        }

        protected void DoOperation(int opCode, int operand)
        {
            _outputWriter.WriteLine("Doing {0} to {1}...", opCode, operand);

            // Do work that might take awhile
        }
    }
}

This code compiles and runs just fine, producing the following output:

Starting work
Doing 1 to 2...
Work complete

However, after that, the application will just sit there waiting until I press Ctrl+C. I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly. It seems as though it's designed for recurring background processes, and not simple console applications like this. However, in an actual application where DoOperation might take 20-30 minutes, I would like to take advantage of the StopAsync method to do cleanup before terminating. I also know I could create the service container myself and all that, but the .NET Core generic host already does a lot of stuff I would want to do anyway. It seems to be the right way to write console applications, but without adding a hosted service that kicks off the actual work, how do I get the app to actually do anything?

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Instead of a hosted service, I would recommend the following;

using (var host = CreateHostBuilder(args).Build())
{
    await host.StartAsync();
    var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();

    // do work here / get your work service ...

    lifetime.StopApplication();
    await host.WaitForShutdownAsync();
}
over 4 years ago · Santiago Trujillo Report

0

I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly.

I agree it does seem odd. I actually always stop the application at the end of all my IHostedService implementations. This is true even for long-running server apps. If a hosted service stops (or faults), then I explicitly want the application to end.

It does feel like this design was unfinished when .NET Core was pushed out. There are parts of the design that are made so that hosted services and their apps can have independent lifetimes, but since they can not be restarted, this just isn't useful in practice. So it ends up feeling like a poor design because the lifetimes can't be independent, but they are independent by default.

All of my hosted services end up tying their lifetime to the application lifetime.

finally
{
  _hostApplicationLifetime.StopApplication();
}
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!