Mastering Custom Configuration Providers in .NET Core: A Comprehensive Guide
Learn how to extend the .NET Core configuration system by building a custom provider for flexible and scalable app configurations.
Introduction
To keep modern apps flexible and able to grow, it’s really important to have a good system for managing their settings and configurations. In .NET Core, you can load configuration from various sources like JSON, XML, environment variables and more. But sometimes, you might need to get configuration from less common places, such as a database, an API or a custom file format.
In this post, we’ll explore how to create a custom configuration provider in .NET Core to handle non-standard configuration sources.
Overview of .NET Core Configuration System
Before jumping into creating a custom configuration provider, let’s review how the configuration system works in .NET Core.
At the core of the configuration system is the IConfiguration interface, which is responsible for managing key-value pairs. You can choose where to get your app’s settings from, like regular text files (JSON), special environment variables and more.
Here’s an example of the standard configuration setup in .NET Core:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.AddUserSecrets(typeof(Program).Assembly, true)
.Build();
string mySetting = configuration["MySetting"];
Understanding Configuration Providers
At its core, a configuration provider is a class that adheres to the IConfigurationProvider interface. This system requires you to create a function called Load that will get the app’s settings from the chosen place. Once loaded, this data becomes accessible through the IConfiguration interface.
Why Create a Custom Configuration Provider?
You might want to create a custom provider for various reasons, such as:
- Custom File Formats: When your application uses configuration files in non-standard formats (e.g., YAML, INI).
- Database Integration: You need to load configurations directly from a database.
- External APIs: You want to fetch configuration from an external API.
- Complex Logic: Your configuration source has special rules or transformations that can’t be handled by the built-in providers.
In this guide, we’ll create a tool that gets app settings from a simple list in the computer’s memory. This can be easily changed to use more complicated sources later.
Creating a Custom Configuration Provider
Let’s break down the process of creating a custom configuration provider in .NET Core:
- Create a custom configuration source.
- Implement the custom configuration provider.
- Creating an Extension Method for Configuration Loading.
- Use the custom provider in your application.
Step 1: Create a Custom Configuration Source
A configuration source is responsible for creating the configuration provider. To create a custom source, we need to implement the IConfigurationSource interface.
Here’s the implementation of a custom configuration source:
public class CustomConfigurationSource : IConfigurationSource
{
private readonly IDictionary<string, string> _data;
public CustomConfigurationSource(IDictionary<string, string> data)
{
_data = data;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new CustomConfigurationProvider(_data);
}
}
In this example, we’re using an in-memory dictionary (IDictionary<string, string>) to hold configuration values. However, you can replace this with logic that loads data from a database, API, or any other source.
Step 2: Create a Custom Configuration Provider
Next, we’ll create the actual configuration provider that loads configuration values from the custom source and provides them to the IConfiguration system.
Our custom provider will inherit from ConfigurationProvider and override the Load method:
public class CustomConfigurationProvider : ConfigurationProvider
{
private readonly IDictionary<string, string> _data;
public CustomConfigurationProvider(IDictionary<string, string> data)
{
_data = data;
}
public override void Load()
{
foreach (var kvp in _data)
{
Data[kvp.Key] = kvp.Value;
}
}
}
In the Load method, we’re populating the Data dictionary, which is used internally by the ConfigurationProvider class to manage configuration values.
Step 3: Create an Extension Method
To make it easier to integrate our custom configuration provider, let’s create an extension method that allows users to add it to the IConfigurationBuilder without directly interacting with the provider.
public static class CustomConfigurationExtensions
{
public static IConfigurationBuilder AddCustomConfiguration(this IConfigurationBuilder builder, IDictionary<string, string> data)
{
return builder.Add(new CustomConfigurationSource(data));
}
}
Now, adding the custom provider becomes as simple as calling AddCustomConfiguration on the ConfigurationBuilder.
Step 4: Use the Custom Configuration Provider
Finally, let’s integrate our custom provider into a .NET Core application:
class Program
{
static void Main(string[] args)
{
var customData = new Dictionary<string, string>
{
{ "MyCustomKey", "CustomValue" },
{ "AnotherKey", "AnotherValue" }
};
var configuration = new ConfigurationBuilder()
.AddCustomConfiguration(customData)
.Build();
Console.WriteLine($"MyCustomKey: {configuration["MyCustomKey"]}");
Console.WriteLine($"AnotherKey: {configuration["AnotherKey"]}");
}
}
In this example, we create a Dictionary<string, string> to hold custom key-value pairs and pass it to the configuration builder via our extension method. The values can then be accessed using the IConfiguration interface.
Extending the Custom Configuration Provider
If you want to load configurations from a database, you can modify the Load method in CustomConfigurationProvider to query the database for configuration values:
public override void Load()
{
using (var connection = new SqlConnection("your-connection-string"))
{
connection.Open();
var command = new SqlCommand("SELECT Key, Value FROM Configuration", connection);
var reader = command.ExecuteReader();
while (reader.Read())
{
Data[reader.GetString(0)] = reader.GetString(1);
}
}
}
This code fetches configuration values from a database table and loads them into the configuration system.
Advanced Configuration Techniques
- Database-Driven Configuration: Fetch settings from a database table, enabling dynamic configuration updates without modifying the application’s code.
- Azure Key Vault Integration: Retrieve sensitive configuration values securely from Azure Key Vault, enhancing your application’s security posture.
- Distributed Configuration: Manage configuration settings across multiple environments using a distributed configuration service.
- Custom Configuration Formats: Parse and load configuration data from custom file formats or even programmatic sources.
Conclusion
Creating a custom configuration provider in .NET Core gives you the flexibility to manage configurations from various non-standard sources like databases, APIs, or custom file formats. By implementing a custom IConfigurationSource and IConfigurationProvider, you can easily extend the configuration system to meet your application’s specific needs.
Now that you have the foundation, feel free to customize this approach for different data sources and requirements. Custom configuration providers in .NET Core are a powerful tool for developers building complex, scalable applications.