Multiple Ways To Access Configurations In .NET Applications

Multiple Ways To Access Configurations In .NET Applications

In .NET applications, the ConfigurationProvider reads the configuration and all the configuration is maintained as a collection of key value pairs. The interface IConfiguration can be injected to the classes wherever the configuration needs to be accessed.

In this article, let’s quickly compile the different ways which can be used to read the configurations.

IConfiguration

This is the simplest method. Just inject IConfiguration in any class, where you want to access the configurations. Then you will have to specify the key that you want to access, inside square brackets as shown below. If the key is nested, the keys are separated by ‘:’ character.

The advantage here is simplicity. But the disadvantage is, you need to know the hierarchy of the JSON properties, in exact sequence.

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
this.configuration = configuration;
}
public IActionResult Index()
{
var mode = configuration["Mode"];
var defaultLogLevel = configuration["Logging:LogLevel:Default"];
var firstSubjectFromArray = configuration["MailFeature:0:Subject"];
return View();
}
}
view raw HomeController.cs hosted with ❤ by GitHub

And let’s say, you want to use same value in multiple classes, you will specify same key in those classes causing duplication of keys, which can be solved by declaring key constants. But still there would be an issue. If the configuration JSON object changes, you will have to compile the code and run it to make sure that there are no errors.

Also, this returns value as string. You might have a section dedicated to let’s say logging, and you may want to have a single class to hold all the settings together, with appropriate data type.

GetValue

This is also to fetch a single value from the configuration. The IConfiguration should be injected in the class where the configuration needs to be accessed. Then ConfigurationBinder.GetValue<T> method can be used. The method has type parameter, meaning the return value would be converted to the intended data type.

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
this.configuration = configuration;
}
public IActionResult Index()
{
var mode = configuration.GetValue<string>("Mode", "default-mode");
var defaultLogLevel = configuration.GetValue<string>("Logging:LogLevel:Default", "Info");
var firstSubjectFromArray = configuration.GetValue<string>("MailFeature:0:Subject", "unknown");
return View();
}
}
view raw HomeController.cs hosted with ❤ by GitHub

This is better than previous method as it can convert the value from configurations to specified type. One more difference is, we can specify default value to return in case the setting is not found.

The cons are – it can be used only to access a single value and you need to specify the configuration key wherever you need to access the configuration.

GetSection

Previous two approaches just read a single property and return its value. This API can be used to get a complete JSON section. IConfiguration.GetSection returns a configuration subsection with the specified subsection key.

If the section that we want to get is nested, then multiple keys separated by colon : character, can be specified. Note that this method never returns NULL. If the section is not found, then empty object is returned.

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
this.configuration = configuration;
}
public IActionResult Index()
{
//// Output JSON –
//// "LogLevel": {
//// "Default": "Information",
//// "Microsoft": "Warning",
//// "Microsoft.Hosting.Lifetime": "Information"
//// }
var logLevelSection = configuration.GetSection("Logging:LogLevel");
return View();
}
}
view raw HomeController.cs hosted with ❤ by GitHub

GetChildren

This API will also return multiple JSON properties / objects. When IConfiguration.GetChildren API is called, it would return all the children JSON properties and objects, as explained in below example.

Note that this method would return a collection populated with key and full JSON path populated. The value field of the returned object would be empty. So, if you want to read the value of a property, you might not need this method.

GetChildren does not populate values.
GetChildren does not populate values.

Exists

This does not return the configuration settings. This API ConfigurationExtensions.Exists can be called to verify the section exists.

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
this.configuration = configuration;
}
public IActionResult Index()
{
var loggingSection = configuration.GetSection("Logging");
// To check if the section has a value or has children.
if (!loggingSection.Exists())
{
throw new System.Exception("logging section does not exist.");
}
return View();
}
}
view raw HomeController.cs hosted with ❤ by GitHub

Bind

This API allows to bind the given object to the JSON object in the configuration, by recursively matching the key names from object to JSON section.

For using this API, you first need to call GetSection, to target a specific section to bind.

// Class to hold mail feature json object
public class MailFeatureSettings
{
public string Type { get; set; }
public string From { get; set; }
public string Subject { get; set; }
}
// Startup.cs code
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// some code…
// bind config settings to an object
List<MailFeatureSettings> mailFeatureSettings = new List<MailFeatureSettings>();
Configuration.GetSection("MailFeature").Bind(mailFeatureSettings);
// register object in DI container so that it can be injected
services.AddSingleton<IList<MailFeatureSettings>>(mailFeatureSettings);
// some code…
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// some code…
}
}
// HomeController.cs
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IList<MailFeatureSettings> mailFeatureSettings;
// Injected the list of mail feature settings
public HomeController(ILogger<HomeController> logger, IList<MailFeatureSettings> mailFeatureSettings)
{
_logger = logger;
this.mailFeatureSettings = mailFeatureSettings;
}
public IActionResult Index()
{
// Use the settings from mail feature settings list
ViewData["Email"] = mailFeatureSettings[0].From;
return View();
}
}
view raw HomeController.cs hosted with ❤ by GitHub

The advantage of this approach, we can get a strongly typed object created from the configurations. This can be done in startup and then this bound object can be injected into individual classes which need the configuration settings.

This API if called from startup, can help to keep the configuration reading code at single place, avoiding duplication and can also reveal the issues sooner in the development cycle.

Options Pattern

Using the Bind API mentioned above, the configuration options pattern can be used. But in my opinion, it is extension to Bind API approach, hence I will not cover it here. It is very important pattern in .NET and so,I will try to cover it in next article.

I hope you found this information useful. Let me know your thoughts.

Leave a Reply

This Post Has One Comment