UPDATE: It appears that there’s now a “Worker Service” template available (thanks Hobby Developer) which scaffolds Dependency Injection, configuration and logging for you. If you wnat a long running service then that’s probably the best option but if you want an intermitantly triggered executable that does a defined task and then closes my solution is still probably preferable.

This is something that strangely doesn’t seem to be that well documented and took me a while to figure out though in the end it’s pretty simple.

All that’s required is to add the following NuGet packages and an appsettings.json file.

  • Microsoft.Extensions.Configuration
  • Microsoft.Extensions.Configuration.FileExtensions
  • Microsoft.Extensions.Configuration.Json
  • Microsoft.Extensions.DependencyInjection

The appsettings.json files “Copy to Output Directory” property should also be set to “Copy if newer” so that the application is able to access it when published.

class Program
{
	public static IConfigurationRoot configuration;

	static int Main(string[] args)
	{
		// Initialize serilog logger
		Log.Logger = new LoggerConfiguration()
			 .WriteTo.Console(Serilog.Events.LogEventLevel.Debug)
			 .MinimumLevel.Debug()
			 .Enrich.FromLogContext()
			 .CreateLogger();

		try
		{
			// Start!
			MainAsync(args).Wait();
			return 0;
		}
		catch
		{
			return 1;
		}
	}

	static async Task MainAsync(string[] args)
	{
		// Create service collection
		Log.Information("Creating service collection");
		ServiceCollection serviceCollection = new ServiceCollection();
		ConfigureServices(serviceCollection);

		// Create service provider
		Log.Information("Building service provider");
		IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();

		// Print connection string to demonstrate configuration object is populated
		Console.WriteLine(configuration.GetConnectionString("DataConnection"));

		try
		{
			Log.Information("Starting service");
			await serviceProvider.GetService<App>().Run();
			Log.Information("Ending service");
		}
		catch (Exception ex)
		{
			Log.Fatal(ex, "Error running service");
			throw ex;
		}
		finally
		{
			Log.CloseAndFlush();
		}
	}

	private static void ConfigureServices(IServiceCollection serviceCollection)
	{
		// Add logging
		serviceCollection.AddSingleton(LoggerFactory.Create(builder =>
		{
			builder
				.AddSerilog(dispose: true);
		}));

		serviceCollection.AddLogging();

		// Build configuration
		configuration = new ConfigurationBuilder()
			.SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
			.AddJsonFile("appsettings.json", false)
			.Build();

		// Add access to generic IConfigurationRoot
		serviceCollection.AddSingleton<IConfigurationRoot>(configuration);

		// Add app
		serviceCollection.AddTransient<App>();
	}
}
{
  "EmailAddresses": [
    "email1@test.com",
    "email2@test.com",
    "email3@test.com"
  ],
  "ConnectionStrings": {
    "DataConnection": "ConnectionStringToSQLDatabase"
  }
}

An example of this in action can be seen here.

In the case of receiving the error “IConfigurationBuilder does not contain a definition for AddJsonFile” just rebuild the project and close and re-open Visual Studio.


1 Comment

jeonghwan · 5 March 2022 at 5:24 am

This good for me.

Leave a Reply

Avatar placeholder

Your email address will not be published.