How to read the appsettings.json configuration file in ASP.NET Core

Published: Monday 15 March 2021

ASP.NET Core allows for a configuration file to be set up that can be read through the application.

By default, the appsettings.json file allows for app settings to be configured for a web application.

C# coding challenges

C# coding challenges

Our online code editor allows you to compile the answer.

We will have a look at the different ways to read the app configuration file into an ASP.NET Core application.

In addition, we will demonstrate ways of validating the app configuration, how to specify database connection strings and setting up an app configuration file in a console application.

Resources

To try out this application, download the code example from our code examples section.

As well as that, check our video tutorial where we test out how to get the settings from the appsettings.json file into our ASP.NET Core application.

The appsettings.json File

With ASP.NET, the configuration file used an XML file. Whereas, with ASP.NET Core, it uses the JSON format.

JSON is a lot more compact. No more having to set lengthy opening and closing tags. And that's not forget schemas!

In-turn, that means that the file is easily to read and is a lot more lightweight.

Here is an example of how it looks:

{
	"Logging": {
		"LogLevel": {
		  "Default": "Information",
		  "Microsoft": "Warning",
		  "Microsoft.Hosting.Lifetime": "Information"
		}
	},
	"AllowedHosts": "*"
}

Setting up Custom Configuration

For this part, we are going to set up custom configuration to work in our ASP.NET Core application.

To follow along with this, it's best to create an ASP.NET Core MVC application in Visual Studio 2019.

We are going to set up a RoundTheCodeSync custom configuration. To do this, we added some settings within the appsettings.json file, grouped by a RoundTheCodeSync key.

{
	"Logging": {
		"LogLevel": {
			"Default": "Information",
			"Microsoft": "Warning",
			"Microsoft.Hosting.Lifetime": "Information"
		}
	},
	"AllowedHosts": "*",
	"RoundTheCodeSync": {
		"Title": "Our Sync Tool",
		"Interval": "0:30:00",
		"ConcurrentThreads": 10
	}
}

From there, we can set up an interface and class in our ASP.NET Core MVC application to read our RoundTheCodeSync custom configuration.

public interface IRoundTheCodeSync
{
	string Title { get; set; }

	TimeSpan Interval { get; set; }

	int ConcurrentThreads { get; set; }
}
public class RoundTheCodeSync : IRoundTheCodeSync
{
	public string Title { get; set; }

	public TimeSpan Interval { get; set; }

	public int ConcurrentThreads { get; set; }
}

Reading our Custom Configuration

There are a number of ways we can read our custom configuration into our ASP.NET Core application.

To demonstrate this, we are going to create an AppSettingsController in our ASP.NET Core MVC application.

#1: IConfiguration Instance

The first way is to pass the IConfiguration instance into our AppSettingsController constructor.

// AppSettingsController.cs
[Route("appsettings")]
public class AppSettingsController : Controller
{
	protected readonly IConfiguration _configuration;

	public AppSettingsController(IConfiguration configuration)
	{
		_configuration = configuration;
	}
}

With the IConfiguration instance, we can read the Title property.

To do this, we have created a FirstWay action and used the IConfiguration instance to return the title.

// AppSettingsController.cs
[Route("appsettings")]
public class AppSettingsController : Controller
{
	...

	[Route("first-way")]
	public IActionResult FirstWay()
	{
		return Content(_configuration.GetSection("RoundTheCodeSync").GetChildren().FirstOrDefault(config => config.Key == "Title").Value, "text/plain");
	}
}

However this can be simplified by simply returning the GetValue method in our IConfiguration instance:

// AppSettingsController.cs
[Route("appsettings")]
public class AppSettingsController : Controller
{
	...

	[Route("first-way")]
	public IActionResult FirstWay()
	{
		return Content(_configuration.GetValue<string>("RoundTheCodeSync:Title"), "text/plain");
	}
}

#2: Options Pattern

The second way is to add the custom configuration using the options pattern.

From there, we can pass the IOptions interface through dependency injection, using our custom class as the generic type.

The first thing we need to do is to add the service.

We do that by going into our Startup class and going to the ConfigureServices method.

// Startup.cs
public class Startup
{
	...

	// This method gets called by the runtime. Use this method to add services to the container.
	public void ConfigureServices(IServiceCollection services)
	{
		...

		services.AddOptions<RoundTheCodeSync>().Bind(Configuration.GetSection("RoundTheCodeSync"));        
	}
}

Then we can inject it into our AppSettingsController constructor using our IOptions instance.

// AppSettingsController.cs
[Route("appsettings")]
public class AppSettingsController : Controller
{
	...
	protected readonly IOptions<RoundTheCodeSync> _roundTheCodeSyncOptions;

	public AppSettingsController(..., IOptions<RoundTheCodeSync> roundTheCodeSyncOptions)
	{
		...
		_roundTheCodeSyncOptions = roundTheCodeSyncOptions;
	}

	...

	[Route("second-way")]
	public IActionResult SecondWay()
	{
		return Content(_roundTheCodeSyncOptions.Value.Title, "text/plain");
	}
}

#3 Add The Configuration as a Singleton

Another way is to bind the configuration file to our RoundTheCodeSync class, then add it to dependency injection as a singleton using the IRoundTheCodeSync interace.

In our Startup class, we need to add the service into DI:

// Startup.cs
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)
	{
		...
		 
		services.AddSingleton<IRoundTheCodeSync>((serviceProvider) =>
		{
			return Configuration.GetSection("RoundTheCodeSync").Get<RoundTheCodeSync>();
		});
		 
	}

	...
}

From there, we can inject our IRoundTheCode sync interface into our AppSettingsController constructor, and call the properties directly.

// AppSettingsController.cs
[Route("appsettings")]
public class AppSettingsController : Controller
{
	...
	protected readonly IRoundTheCodeSync _roundTheCodeSync;

	public AppSettingsController(..., IRoundTheCodeSync roundTheCodeSync)
	{
		...
		_roundTheCodeSync = roundTheCodeSync;
	}

	...

	[Route("third-way")]
	public IActionResult ThirdWay()
	{
		return Content(_roundTheCodeSync.Title, "text/plain");
	}
}

Validating Configuration

When using a custom configuration in ASP.NET Core, there is also the ability to validate the form using data annotations.

What we've done is modified the RoundTheCodeSync class and included validation.

The changes we have made include setting a [Required] data annotation around the Title property.

In addition, we have set a [Range] data annotation around the ConcurrentThreads property between 5 and 15. That means that the ConcurrentThreads property must be between 5 and 15, otherwise it will throw an error.

// RoundTheCodeSync.cs
public class RoundTheCodeSync : IRoundTheCodeSync
{
	[Required]
	public string Title { get; set; }

	public TimeSpan Interval { get; set; }

	[Range(5, 15)]
	public int ConcurrentThreads { get; set; }
}

However, adding the data annotations on their own will not validate the configuration.

We need to make a change in our Startup class.

If we were to pass the configuration with the options pattern (see #2 of Reading our Custom Configuration above), we can append the ValidateDataAnnotations method to it and this will automatically validate our configuration file.

/ Startup.cs
public class Startup
{
	...
	 
	public void ConfigureServices(IServiceCollection services)
	{
		...

		services.AddOptions<RoundTheCodeSync>().Bind(Configuration.GetSection("RoundTheCodeSync")).ValidateDataAnnotations();
		 
		...        
	}

	...
}

But, if we are passing in the configuration as a singleton (see #3 of Reading our Custom Configuration above), we can use the Validator class and call the TryValidateObject method, passing in the configuration file to validate.

If the TryValidateObject method returns false, we can throw our own custom exception.

// Startup.cs
public class Startup
{
	...

	// This method gets called by the runtime. Use this method to add services to the container.
	public void ConfigureServices(IServiceCollection services)
	{
		...
		 
		services.AddSingleton<IRoundTheCodeSync>((serviceProvider) =>
		{
			var config = Configuration.GetSection("RoundTheCodeSync").Get<RoundTheCodeSync>();

			var valid = Validator.TryValidateObject(config, new ValidationContext(config), new List<ValidationResult>(), true);

			if (!valid)
			{
				throw new Exception("Not all settings are valid.");
			}

			return config;
		});
		 
	}

	...
}

Creating a Seperate Configuration File

Having a configuration with many settings can be confusing.

This could particularly be the case if we had a large application to maintain.

Fortunately, there is a way in ASP.NET Core to separate the configuration files away from the main appsettings.json file.

What we would do is to move the RoundTheCodeSync configuration out of appsettings.json and into it's own file, which we will call roundthecodesync.json.

{
	"RoundTheCodeSync": {
		"Title": "This is the sync",
		"Interval": "0:30:00",
		"ConcurrentThreads": 12
	}
}

After that, we need to go into our Program class.

When building up our host instance, we can call the ConfigureAppConfiguration method. Within that, we pass an instance of IConfigurationBuilder. The IConfigurationBuilder interface has a AddJsonFile method, and this is where we can specify our extra configuration file.

{
	...

	public static IHostBuilder CreateHostBuilder(string[] args) =>
		Host.CreateDefaultBuilder(args)
		.ConfigureAppConfiguration((hostingContext, config) =>
		{
			config.AddJsonFile("RoundTheCodeSync.json",
				optional: true,
				reloadOnChange: true);
		})
		.ConfigureWebHostDefaults(webBuilder =>
		{
			webBuilder.UseStartup<Startup>();
		});
}

Importing Connection Strings Configuration

ASP.NET Core allows for configuring connection strings.

Quite simply, we specify our connection strings inside the ConnectionStrings key in our appsettings.json file.

{
	...
	"ConnectionStrings": {
		"SyncDb": "Server=localhost; Database=RoundTheCodeSync; Trusted_Connection=true; MultipleActiveResultSets=true; Integrated Security=true;"
	}
}

From there, we can use our IConfiguration instance, and call the GetConnectionString method to get data from our database.

This is an example of how we can use it.

// AppSettingsController.cs
public class AppSettingsController : Controller
{
	...

	[Route("database")]
	public async Task<IActionResult> Database()
	{
		var name = string.Empty;

		using (var conn = new SqlConnection(_configuration.GetConnectionString("SyncDb")))
		{
			await conn.OpenAsync();

			using (var cmd = new SqlCommand("SELECT * FROM dbo.Sync WHERE ID = 1", conn))
			{
				using (var reader = await cmd.ExecuteReaderAsync())
				{
					if (await reader.ReadAsync())
					{
						name = reader["Name"].ToString();
					}
				}
			}
		}

		return Content(name, "text/plain");
	}
}

Using appsettings.json in a Console Application

It's possible to use the appsettings.json configuration file in a console application.

In-order to do that, we will need to install the following Nuget packages in our console application to work.

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Options
Newtonsoft.Json

From there, in our console application, we can build up an instance of ConfigurationBuilder, using our appsettings.json file.

We can also validate the data annotations as well to check that our configuration file is valid.

// Program.cs
class Program
{
	static void Main(string[] args)
	{
		var config = new ConfigurationBuilder()                
			.AddJsonFile("appsettings.json").Build();

		var roundTheCodeSync = config.GetSection("RoundTheCodeSync").Get<RoundTheCodeSync>();

		if (!Validator.TryValidateObject(roundTheCodeSync, new ValidationContext(roundTheCodeSync), new List<ValidationResult>(), true)) {
			throw new Exception("Unable to find all settings");
		}
		 
		Console.WriteLine(roundTheCodeSync.Title);
		Console.WriteLine(roundTheCodeSync.Interval);
		Console.WriteLine(roundTheCodeSync.ConcurrentThreads);
	}
} 

Just one thing to note, in a console application, we need to make sure that the appsettings.json file is being copied to the output directory.

Otherwise, it will not be able to read the file, and will throw an error.

Good Support

So that's how to get going with ASP.NET Core's configuration.

It's good that we can bind settings to strongly typed classes and validate them using data annotations.

In addition, we can separate each group of settings into an individual file and class.

So, it has good support whether we are writing a small application, or one that is a lot more complex.