X

Read configuration value from appsettings.json in ASP.Net Core

Dung Do Tien Apr 17 2020 8108
In the Asp.Net web app or API to read config keys is very necessary. In this article, I will introduce to you the best way to read configuration value from appsettings.json in ASP.Net Core. I hope it will help you easily to learn Asp.net core.

In all versions of .net, we also see they have config files. From WebForm to MVC 4 we have a web.config file format in XML syntax, this file is very important to config value for some package, version, connection string and custom config value to use when an application running. When we code we have many values needed to fix and use it in more like code but it’s not constant to hand code. If you hand-code it, it will be hard to change when the application is running, also related to maintaining source code. Such as metadata information of a page, some config API (URL, param ...) ...vv
All above is of Asp.Net Framework. Today we will code with another version of .Net. It is Asp.Net Core.

In Asp.Net Core we also have a config file with the same purpose but it is more flexible. It is appsettings.json format in JSON syntax. You can create many appsettings files for many environments like Development, Testing, Product …

In this article, I will show you the best way to read the value from appsettings.json in ASP.Net Core and I use version Asp.net core 3.1 to write an example but it is still working fine with versions 2.2, 3.0 or maybe above 3.1 in the future.

1. Create a project and add config value

Now, to begin we need Visual Studio 2017 or above. I recommend using VS 2019 because it has built-in version 3.1 when you install it.

We will create the ASP.NET Core project first. Open  Visual Studio editor and press Ctrl+Shift+N

Select project type is ASP.NET Core Web Application  and click Next button below.

Type name of your project, select location to create project and click Create button.

In this step, you can select a version dot net core of your project. The latest version is default and you have to choose the type of project. in this example, I selected the Web Application option and click Create button.

After creating a project you can check the current version of the project by way select project from Solution Explorer panel and press Alt + Enter or right-click the project and select Properties option.
From the Application tab, you check Target framework Combobox and it is the current version of your project. You also change it to another version here.

Now we start open to adding more value to appsettings.json file to read it.

- Add a connection string to connect database:

- And add more  config key to send an email:

And now we need to load this file when the application starts. Open Startup.cs file and we’re starting now.

You need to create a constructor of Startup objects. This constructor will initial once a time when starting the app as below :

public Startup(IHostingEnvironment evm)
{
    var builder = new ConfigurationBuilder()
      .SetBasePath(evm.ContentRootPath)
      .AddJsonFile("appsettings.json", true, true)
      .AddJsonFile($"appsettings.{evm.EnvironmentName}.json", true)
      .AddEnvironmentVariables();
    Configuration = builder.Build(); // load all file config to Configuration property 
    AppSettings.Instance.SetConfiguration(Configuration); //        
}

Configuration : This property contains all values of appsettings file.

AppSettings.Instance.SetConfiguration(Configuration):  this line code is initial IConfiguration contains data in appsettings file. This line will get an error but follow below to create this object.

- To get many types of values in appsettings file and we can use & reuse it anywhere I create an AppSettings object to do it. In this object, some method corresponds to the data type in appsettings file.

2. Create an AppSettings class file

So this file will be used from many places in the project so I used the Singleton pattern to help reduce the number of times the object initialization.

You have to install 2 packages below by use Nuget:

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Binder
public class AppSettings
{
    private static AppSettings _instance;
    private static readonly object ObjLocked = new object();
    private IConfiguration _configuration;

    protected AppSettings()
    {
    }
    
    public static AppSettings Instance
    {
        get
        {
            if (null == _instance)
            {
                lock (ObjLocked)
                {
                    if (null == _instance)
                        _instance = new AppSettings();
                }
            }
            return _instance;
        }
    }
}

To understand more about Singleton's design pattern you can read more from here.

Now we need to create a method SetConfiguration() to initialize before getting the value by key.

public void SetConfiguration(IConfiguration configuration)
{
    _configuration = configuration;
}

IConfiguration: This interface inherits from package Microsoft.Extensions.Configuration.

And I create some methods to get connection string, int, string, bool, object …

public string GetConnection(string key, string defaultValue = "")
{
    try
    {
        return _configuration.GetConnectionString(key);
    }
    catch
    {
        return defaultValue;
    }
}

public int GetInt32(string key, int defaultValue = 0)
{
    try
    {
        return _configuration.GetSection("StringValue").GetChildren().FirstOrDefault(x => x.Key == key).Value.ToInt();
    }
    catch
    {
        return defaultValue;
    }
}

// ….. Add many methods if you want here

Okay, I have summed up all the functions I need for the application below as completely as possible. If you need more to get other data types you can add them at the end of the class.

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ReadConfig.Bsl
{
    public class AppSettings
    {
        private static AppSettings _instance;
        private static readonly object ObjLocked = new object();
        private IConfiguration _configuration;

        protected AppSettings()
        {
        }

        public void SetConfiguration(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public static AppSettings Instance
        {
            get
            {
                if (null == _instance)
                {
                    lock (ObjLocked)
                    {
                        if (null == _instance)
                            _instance = new AppSettings();
                    }
                }
                return _instance;
            }
        }

        public bool GetBool(string key, bool defaultValue = false)
        {
            try
            {
                return bool.Parse(_configuration.GetSection("StringValue").GetChildren().FirstOrDefault(x => x.Key == key).Value);
            }
            catch
            {
                return defaultValue;
            }
        }

        public string GetConnection(string key, string defaultValue = "")
        {
            try
            {
                return _configuration.GetConnectionString(key);
            }
            catch
            {
                return defaultValue;
            }
        }

        public int GetInt32(string key, int defaultValue = 0)
        {
            try
            {
                return Int32.Parse(_configuration.GetSection("StringValue").GetChildren().FirstOrDefault(x => x.Key == key).Value);
            }
            catch
            {
                return defaultValue;
            }
        }

        public long GetInt64(string key, long defaultValue = 0L)
        {
            try
            {
                return Int64.Parse(_configuration.GetSection("StringValue").GetChildren().FirstOrDefault(x => x.Key == key).Value);
            }
            catch
            {
                return defaultValue;
            }
        }

        public string GetString(string key, string defaultValue = "")
        {
            try
            {
                var value = _configuration.GetSection("StringValue").GetChildren().FirstOrDefault(x => x.Key == key)?.Value;
                return string.IsNullOrEmpty(value) ? defaultValue : value;
            }
            catch
            {
                return defaultValue;
            }
        }

        public T Get<T>(string key = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                return _configuration.Get<T>();
            else
                return _configuration.GetSection(key).Get<T>();
        }

        public T Get<T>(string key, T defaultValue)
        {
            if (_configuration.GetSection(key) == null)
                return defaultValue;

            if (string.IsNullOrWhiteSpace(key))
                return _configuration.Get<T>();
            else
                return _configuration.GetSection(key).Get<T>();
        }

        public static T GetObject<T>(string key = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                return Instance._configuration.Get<T>();
            else
            {
                var section = Instance._configuration.GetSection(key);
                return section.Get<T>();
            }
        }

        public static T GetObject<T>(string key, T defaultValue)
        {
            if (Instance._configuration.GetSection(key) == null)
                return defaultValue;

            if (string.IsNullOrWhiteSpace(key))
                return Instance._configuration.Get<T>();
            else
                return Instance._configuration.GetSection(key).Get<T>();
        }
    }
}

Well. So we have done steps to create an object to read config files by key. Now we try to call it and get value.

3. Try Read value by key and customs

We need to create a controller to call it. And below code I create HomeControler.cs with Index action: 

public class HomeController : Controller
{
    public IActionResult Index()
    {
        var connectionString = AppSettings.Instance.GetConnection("ConnectionString");
        var emailSender = AppSettings.Instance.Get<string>("MailConfig:Sender:Email");
        var emailHost = AppSettings.Instance.Get<string>("MailConfig:Servers:MailGun:Host");

        string returnText = " 1. Connection String \n";
        returnText += "  " +connectionString;
        returnText += "\n 2. Email info";
        returnText += "\n Sender : " + emailSender;
        returnText += "\n Host : " + emailHost;

        return Content(returnText);
    }
}

And this is the result :

Okay, it works very well for me.

AppSettings.Instance.Get<string>("MailConfig:Servers:MailGun:Host")

But how do you feel about this line code and when we want to get more value from an object in this way? For example, with the MailConfig key, we have 5 information needed to get value and we have to declare 5 variables. Wow, it's too long and it is not a good idea.

So the best way is to create an object and get by the object. We will get one line code instead of 5 lines code as above.

I will create MailConfig object as below : 

namespace ReadConfig.Model
{
    public class MailConfig
    {
        public Servers Servers { get; set; }
        public Sender Sender { get; set; }
    }

    public class Servers
    {
        public MailGun MailGun { get; set; }
    }
    
    public class MailGun
    {
        public string Pass { get; set; }
        public int Port { get; set; }
        public string Host { get; set; }
    }
    
    public class Sender
    {
        public string Email { get; set; }
        public string Pass { get; set; }
    }
}

And call to get the value of all properties of this object and code look like below :

public IActionResult Index()
{
    var emailHost = AppSettings.Instance.Get<MailConfig>("MailConfig");

    string returnText = string.Format("1. Pass : {0} \n", emailHost.Servers.MailGun.Pass);
    returnText += string.Format("2. Port : {0} \n", emailHost.Servers.MailGun.Port);
    returnText += string.Format("3. Host : {0} \n", emailHost.Servers.MailGun.Host);
    returnText += string.Format("4. Email : {0} \n", emailHost.Sender.Email);
    returnText += string.Format("5. Pass : {0} \n", emailHost.Sender.Pass);

    return Content(returnText);
}

And this is the result:

4. Setting environment appsettings file

In the appsettings.json file, you can create more files with an extension environment name. For example, in my follow coding I have three steps, the first I have to develop and then deploy for Tester execute unit test case and final I have to deploy code to hosting public code to the internet. It corresponds to the three environments Development Testing and Production.

So I will create more three file appsettings as the image below: 

The appsettings.json file is default file if the current environment not set that key will read from appsettings.json.

How to set the environment?

If you are coding and run in local you can select the project from Solution Explorer panel and press Alt + Enter or right-click the project and select Properties option.
From the Debug tab, you will see the environment variables with Name/value. You can change the value here.

If you deploy to Docker you can set in the docker file

ENTRYPOINT ["dotnet", "WebApp.dll","--environment=Development"]

Default is production mode when deploying into Docker.

Why have to set more environments?

Because the value of a key may vary across environments so if you only have file appsettings.json you will have to change the value for that environment. It very inconveniences so we need to make more files to solve this issue.

5. Summary

In this article, I only show one way to read configuration values from appsettings.json in ASP.Net Core but I think this is the best way to do it because it is easy to use, easy maintain and extend code. You don’t forget this way also use well to read configuration values from appsettings.json in ASP.Net Core API.

This is my only option and you also customize it to suit your project. If you need any questions, please leave a comment below and we discuss.

You also download all source examples for reference from Github in here.