UPDATE: It appears that there’s now a “Worker Service” template available (thanks Hobby Developer) which scaffolds Dependency Injection, configuration and logging so it’s probably a better idea to use that than adding in the fuctionality manually as done below.

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.

The settings are injected in the main method rather than in the startup method as with web apps but the code is essentially the same.

Program.cs

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>();
	}
}

appsettings.json

{
  "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.


45 Comments

Sergey · 17th March 2018 at 10:28 am

Thank you for sharing this!

gerardo · 22nd March 2018 at 8:01 pm

cool post! heads up i had to set appsettings.json property Copy to Output Directory to Copy If Newer for mine to work

    Shinigami · 23rd March 2018 at 9:44 am

    Thanks Gerardo, I always forget about this! Have updated the post to mention it.

murat mert · 10th April 2018 at 3:31 pm

hi,
what if there are other enviroments, i mean how can i distinguish other environments like “appsettings.*.json”.Another question does support array value of key.

    Shinigami · 11th April 2018 at 9:35 am

    Hi Murat,
    The below is used in the web application template to load the settings based on the environment.

    .AddJsonFile($”appsettings.{Environment.GetEnvironmentVariable(“ASPNETCORE_ENVIRONMENT”) ?? “Production”}.json”, optional: true)

    It looks like System.Enviroment is usable in console applications so you may be able to use this to determine what enviroment your application is running in.

PinoyDev · 9th May 2018 at 4:00 pm

Thank you so much for this!! great help

Christian Aranda · 15th September 2018 at 10:23 pm

Many thanks!!!!

Ed · 12th October 2018 at 3:57 am

Great post, thanks for the help! It is the same thing also for adding .AddEnvironmentVariables() -> Microsoft.Extensions.Configuration.EnvironmentVariables ­čÖé

luis · 31st October 2018 at 10:05 pm

Thanks for sharing!

Marcel · 7th November 2018 at 8:03 am

Hi thanks for this. How do you use it in another class? I use a static reference, but it feels kind of odd. Do you have a better idea?

Eric · 8th November 2018 at 8:23 pm

I really appreciate this. I couldn’t find it anywhere in any of Microsoft’s tutorials. I don’t know how MS expects anyone to claim .Net Core cuts dev cycles if they don’t publish really important stuff like this.

Kwesi Dadson · 20th November 2018 at 9:26 am

Thank you very very much. This is just what I needed!

Johan Bj├Ârklund · 20th November 2018 at 9:23 pm

Jolly good m8, thanks a bunch!

Les Kinney · 28th November 2018 at 4:44 pm

I’ve implemented this solution on my development machine using VS 2017 and .net core 2.1. However, when I try to build it on my build server, it fails because it requires .net Standard. In checking dependencies on the Microsoft.Extensions.Configuration packages they do require .Net Standard. Is there no way to use configuration files(setting) on just .Net Core?

    Shinigami · 29th November 2018 at 9:58 am

    Hi Les, I didn’t realise it had a dependency on .NET Standard but as Microsoft.Extensions.Configuration contains the IConfigurationRoot object I don’t think you can get away without it. .NET Standard will need to be installed on your build machine to allow you to build your solution, however if you publish it on your live machine all dependencies should be output to the publish folder when the project is published so as long as you copy the whole thing over you should be able to run your dll.

Krakor · 3rd December 2018 at 3:21 pm

T’es juste le meilleur en fait

Chad Kuehn · 4th January 2019 at 5:54 am

Helpful. Thanks!

MIke May · 8th January 2019 at 8:57 am

Many thanks. Very useful

Yasin Co┼čkun · 20th January 2019 at 10:41 pm

Hello that was very helpful but i’m really new to start coding in a empty project so i dont even know how to put mu connection string in this json file can you please help me

    Shinigami · 21st January 2019 at 9:41 am

    The appsettings.json file is just a standard JSON file in the project root. I’ve described this a bit more in the readme for the project mentioned above.

    https://github.com/mattosaurus/AzureBackup

    If you create a brand new web application project then you’ll see it has an appsettings.json file included which may help you understand how it works.

ssurba · 15th February 2019 at 4:38 pm

This is brilliant, thank you.

Deepak · 6th March 2019 at 2:10 am

Can you please share your json file as well.. need to see how does this looks like..

    Shinigami · 6th March 2019 at 9:26 am

    I’ve added an example appsettings.json file to the post.

P Fins · 19th March 2019 at 2:14 pm

Thanks. Super helpful. And still applicable to Asp.net Core 3.0… (Just in case someone else is looking…)

F Abbas · 9th May 2019 at 4:05 pm

Thanks for sharing

Brad · 16th July 2019 at 5:01 pm

I know there must be a reason, but why is this so hard in core? Every other coding framework has a way to do this easily.

Chris · 31st August 2019 at 8:05 pm

ditto all the other comments. Thank you so much for posting this. It’s helped me out immensely. Thanks!

Ken · 3rd September 2019 at 8:14 pm

Thanks for sharing!

Jenny · 18th September 2019 at 3:57 am

Thank you so much for posting! Saved me hours

Vishal · 25th September 2019 at 7:51 am

Thanks for sharing this. Saved me a ton of time figuring it out!

Jason Payne · 26th October 2019 at 11:35 pm

Very useful article. It’s also useful to know that if you want to use binding extension methods such as Get in addition to GetSection, then you would need to add the NuGet package Microsoft.Extensions.Configuration.Binder which you normally get for “free” with an ASP.NET Core application.

Roger · 9th January 2020 at 4:09 pm

Has this already changed for .net Core 3? I have the error that ConfigurationBuilder.SetBasePath() is not resolving even with the 3 nuget packages added. Can you share the using statements for Program.cs?

    Shinigami · 9th January 2020 at 5:26 pm

    Nope, seems to work the same in 3.1, the using statement is below.

    using Microsoft.Extensions.Configuration;
    using System;
    using System.IO;

    There’s a full example here which might help, it also demonstrates how to use dependency injection to access the configuration object in other classes.

    https://github.com/mattosaurus/BitscryExamples/tree/master/ConsoleAppSettings

Chris · 14th February 2020 at 1:48 pm

Be aware this is read-only Microsoft has not provided a way for you to save anything using the ConfigurationBuilder

Carlos · 26th March 2020 at 11:11 pm

Hey firts of all thanks for sharing this.
I’m following your example and in the section Add logging i’m having the following error

…”‘LoggerFactory’ does not contain a definition for ‘AddConsole’ and no accessble extension method ‘AddConsole’ accepting a first argument of type ‘LoggerFactory’ could be found”…

same for .AddSerilog() method.

Any hint on this?
not sure if I’m missing something. I’ve got all dependencies from the example appart from
using AzureBackup.Services;
Thanks

Ryan S Robinson · 13th April 2020 at 3:51 pm

Simple and effective. Great job, appreciate you sharing this tip and for Shinigami for sharing the .NET Core 3 sample reference.

k. · 3rd May 2020 at 7:54 pm

Hi!
I appreciate this article. Question though: What using statement do I need to be able to access the “App” type in the line ‘//serviceCollection.AddTransient();’? I tried searching dotnetcore app type, but as you can imagine this doesn’t yield useful results.

Thanks for your time!

k.

    Shinigami · 4th May 2020 at 10:00 am

    This is an extension method to the IServiceCollection and comes from Microsoft.Extensions.DependencyInjection. I’m not sure if this pacake has been recently split out or I just forgot to add it to the list above but I’ve ammended the post now.

Vicky · 19th May 2020 at 10:07 pm

How we use the configuration in other DLLs in the project? Can you please provide me code snippet?

Is it possible to use without injecting into the constructor?

Thank You

    Shinigami · 20th May 2020 at 9:38 am

    If you want to use configuration in other classes without injecting it then you’ll need to recreate the Configuration object each time.

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

    You could also use the options pattern to set the config properties on your services when they’re created.

    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1

Naveed Butt · 20th July 2020 at 3:51 pm

Thanks a lot on this. It really helped.

Hobby Developer · 18th October 2020 at 5:52 pm

Hi,

This may have been a good solution in 2017. But after some research I found out that the MSFT has introduced a “Worker Service” project template which provides a very convenient hosting environment. All you need to do is implement the “Worker” class to do you work and to gracefully shutdown capture the IHostApplicationLifetime in the constructor of the Worker class and call IHostApplicationLifetime.StopApplication() when you are finished to shutdown gracefully giving you a Console App functionality with all the bells & whistles of DI, ConfigurationServices etc without much effort.

Hope it helps…

Cheers…

    Shinigami · 18th October 2020 at 7:12 pm

    Awesome, it’s about time they made this a bit easier to implement. I’ll check this out as it certainly sounds like a better way of doing things.

Leave a Reply to Krakor Cancel reply

Your email address will not be published. Required fields are marked *