7

I'm using .NET Core 3.1.

Microsoft offers comprehensive guide about how to work with configuration for ASP.NET. However, I can't find any guide on how to use configuration in a .NET Core application, which doesn't use ASP.NET.

How do I access configuration files from C# console application?

2
  • 1
    How would you feel about using the WorkerService template instead? It can be run as a console or installed as a service/daemon. The nice thing about it is it comes with dependency injection and configuration services right out of the box Commented Jun 17, 2020 at 14:48
  • @WBuck WorkerService looks good! I'll keep it in mind. Commented Jun 18, 2020 at 8:04

5 Answers 5

11

Configuration functionality is not built in to .NET Core (obviously). You will need to inject it, using Nuget and some startup configuration. Essentially you will need to register your configuration. Here is how...

Install the NuGet packages:

Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Microsoft.Extensions.Configuration.CommandLine
Install-Package Microsoft.Extensions.Configuration.EnvironmentVariables 

Add an appsettings.json file to your project at the root level. You'll need to register it like so:

static async Task Main(string[] args)
{

  IConfiguration Configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();
}

Now that your providers are registered with your console app, you can reference your configuration like so:

var section = Configuration.GetSection("MySectionOfSettings");
var configValue = Configuration.GetValue("MySetting");
Sign up to request clarification or add additional context in comments.

Comments

8

There isn't that much difference between the two (how it works in a dotnet-core "app" vs how it works in aspnet-core).

Now, I am going to assume that you don't simply want to console.write the values out, but you are eventually are going to want to have access to them inside of some real code....the below uses "UserController" as the code that needs access to your config values.

Ok, the big thing with dotnet core is you INJECT configuration (usually a POCO class) (see IOptions later in this answer for more options) into your code that needs the configuration. You do not "grab a value from app.config or web.config" like you may have done in dotnet framework. Please re-read that and use it to change your thinking from dotnet-framework. I cannot emphasize this enough. Your "real code" that needs config values should not go out and read a file....you should INJECT the poco (or IOptions<MyPoco>) into your class.

So this is a typical setup that I do in a dotnet core console application.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public static class Program
{
    public static int Main(string[] args)
    {
        try
        {
            /* look at the Project-Properties/Debug(Tab) for this environment variable */
            string environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
            Console.WriteLine(string.Format("DOTNET_ENVIRONMENT='{0}'", environmentName));
            Console.WriteLine(string.Empty);

            IConfigurationBuilder builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .AddJsonFile($"appsettings.{environmentName}.json", true, true)
                    .AddEnvironmentVariables();
            /* pay attention to the "true, true" arguments above, they reflect optional: true, reloadOnChange: true ..  "optional" being the important one here */


            IConfigurationRoot myconfig = builder.Build();

            IServiceProvider servicesProvider = BuildDi(myconfig );
            using (servicesProvider as IDisposable)
            {
                /* now do something with your properly composed IoC container */

                /* maybe something like this...*/
                IUserController uc = servicesProvider.GetRequiredService<IUserController>();

                 /* UserController will have the email-config injected into it, because you did the IoC container setup correctly in "BuildDi" method .. later in this answer */

                Console.WriteLine("Press ANY key to exit");
                Console.ReadLine();
            }

============================

So, what is that line

.AddJsonFile($"appsettings.{environmentName}.json"

all about?

First, it is OPTIONAL. You do not have to use it. But I am including it in this answer to provide a holistic answer. (if you don't want to use it, just remove or comment-out the code-line '.AddJsonFile($"appsettings.{environmentName}.json", true, true)'.

Second, is it a best practice? Hmm. For Development is is not so bad. But do you want to have your production config-values that are SECRETS in your .json file? That discussion is outside the scope of the original question and this answer.

But if you (decide to) use it.

There is a magic ENVIRONMENT variable to drive things.

One of the few places I found a different name for .net core console applications is here:

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-3.1

So for asp.net core apps, I set

ASPNETCORE_ENVIRONMENT 

for dotnet core console apps, I set

DOTNET_ENVIRONMENT

============

So with the above, you must have at least a "appsettings.json" file.

But you can optionally have these files.

appsettings.Development.json
appsettings.Staging.json
appsettings.Production.json

You can read more about that stuff here: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/

Now, how do you get those values that are in .json files into your code that needs them.

In dotnet-core, configuration poco's are usually injected into the classes.

https://medium.com/@dozieogbo/a-better-way-to-inject-appsettings-in-asp-net-core-96be36ffa22b

The above shows a good example...

=======================

public class EmailConfig {
    public string Username { get; set;}  
    public string Password { get; set;} 
}

=======================

public class UserController : IUserController
{
    private readonly EmailConfig _emailConfig;

    public UserController(EmailConfig emailConfig)
    {
        _emailConfig = emailConfig;
    
        //_emailConfig.Username
        //_emailConfig.Password
    }
}

and the .json will be appended with your extra custom configuration section/area......following the same example, the below shows a default .json file...BUT WITH "Email" section added in.

"ConnectionStrings": {
  "DefaultConnection": ""
},
"Logging": {
  "IncludeScopes": false,
  "LogLevel": {
    "Default": "Warning"
  }
},
"Email": {
  "Username": "peanut",
  "Password": "butter"
}

=======================

=======================

    private static IServiceProvider BuildDi(IConfiguration myconfig)
    {


        ////setup our DI
        IServiceCollection servColl = new ServiceCollection()
            .AddLogging();  /* do any other stuff here that is extension method */


        EmailConfig emailConfig = new EmailConfig();
        myconfig.GetSection("Email").Bind(emailConfig);
        /* you may want to check for null here on emailConfig */
    
        //Create singleton from instance..and push to the IoC container
        servColl.AddSingleton<EmailConfig>(emailConfig);

         /* not shown here.. but you'll need to add IUserController,UserController as well */

        ServiceProvider servProv = servColl.BuildServiceProvider();

        return servProv;
    }

and germane csproj values as well.

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.*" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.*" />
  </ItemGroup>

Now BONUS material

https://medium.com/@kmar.ayush/eli5-ioptions-vs-ioptionssnaphot-vs-ioptionsmonitor-fab1d7e26a75

Or internet search "ioptions vs ioptionssnaphot vs ioptionsmonitor"

You can also setup your POCO configurations with one of the above "Option"'s.

IOptions<T>
When using this, the values will always be same for the lifetime of the application. Changing the value of the appsettings file while the application is running will not make any difference to the value when using IOptions. In other words it is in Singleton scope.

IOptionsSnapshot<T>
Using this will always give you the most up to date values in your config file for every request and will remain the same for the during of your request.


IOptionsMonitor<T>
Using the monitor is almost like having real time access to your settings file. Each time you need the value from the config, it will read it and evaluate whatever is there and return that.

................

One of the advantages of injecting a Poco configuration object into your "real code" .. is that for unit tests .. EmailConfig can be "coded up" without backing .config/.json files). Aka, you can test your code without needing a json file..and run the test completely in-memory.


Finally, here is a small sample that shows how to actually "get at" that "default is 'Production'" value when no environment variables are set:

    public static class Program
    {
        static void Main(string[] args)
        {

            string environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
            Console.WriteLine(string.Format("DOTNET_ENVIRONMENT='{0}'", environmentName));
            Console.WriteLine(string.Empty);

            environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            Console.WriteLine(string.Format("ASPNETCORE_ENVIRONMENT='{0}'", environmentName));
            Console.WriteLine(string.Empty);


            /* both empty strings above */

            Microsoft.Extensions.Hosting.HostBuilder builder = new Microsoft.Extensions.Hosting.HostBuilder();
            builder
            .ConfigureHostConfiguration(config =>
            {
                config.AddEnvironmentVariables(prefix: "DOTNETCORE_");
            })
             .ConfigureAppConfiguration((context, config) =>
             {
                 config.AddJsonFile("appsettings.json", optional: true);
                 config.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
             });


            Microsoft.Extensions.Hosting.IHost hst = builder.Build();

            /* do NOT do the below in real code..this is cheap trick to show EnvironmentName default */
            Microsoft.Extensions.Hosting.IHostEnvironment env = hst.Services.GetService(typeof(Microsoft.Extensions.Hosting.IHostEnvironment)) as Microsoft.Extensions.Hosting.IHostEnvironment;
            if (null != env)
            {
                Console.WriteLine(env.EnvironmentName);
            }
}

and germane csproj parts

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.*" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.*" />

  </ItemGroup>

Output:

DOTNET_ENVIRONMENT=''

ASPNETCORE_ENVIRONMENT=''

Production

3 Comments

Thank you for thorough answer. You use environment variable to define environment, but since my app is not a web application, but application that is going to be distributed, I don't have any control over environment variables on machines of end users, right? Therefore, I need to consider undefined environment variable as production.
You don't have to use the environment variable thing. Just remove add specific AddJsonFile line. And code that reads the setting of course.
Nothing I wrote in my answer is specifically about a "web application". I wrote the answer geared toward a console application (because I've been through the fire with them). I'm just trying to connect a few dots in the overall picture.
1

What you could have an appsettings.json file. Then you could have a class for representing this file, like:

public class Settings
{
    public string ConnectionString { get; set; }
}

Then, in the main method you can read your Json file and deserialize it using System.Text.Json into the class you need, like:

using (var fs = File.OpenRead(fileName))
{
    var settings = JsonSerializer.Deserialize<Settings>(fs);
    // set this in a accessible variable
}

1 Comment

So just a friendly comment. DotnetCore has kinda worked this functionality in already. So this is mini re-inventing the wheel. This is a good answer for "how to create a poco from a json file"..but for .appsettings specifically, this is a small reinvention.
0

We have our own configuration subsystem that searches for a set of configuration files AND can use the registry to be redirected to a specific configuration. There is nothing stopping you from basically writing your own configuration subsystem - first version was like 4 hours of work.

Comments

0

Top answer from AussieJoe above is good, except it wouldn't work without doing these:

  1. Add Nuget package Microsoft.Extensions.Configuration.Binder

  2. Change var configValue = Configuration.GetValue("MySetting"); to var configValue = Configuration.GetValue("MySetting");

  3. On the new ConfigurationBuilder(), add this as welL .SetBasePath(Directory.GetCurrentDirectory())

In other words: IConfiguration Configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .AddCommandLine(args) .SetBasePath(Directory.GetCurrentDirectory()) //Add this line! .Build();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.