ASP.NET Core Configuration

2024-07-01

筆記 ASP.NET Core Configuration 的基本要點。

logo

Configuration

We add a model first to map the configuration.

public class WebsiteProfileOptions
{
    public string ThemeColor { get; set; } = "green";
    public string FontSize { get; set; } = "16px";
}
[Route("api/[controller]")]
[ApiController]
public class OptionsController : ControllerBase
{
    readonly IOptions<WebsiteProfileOptions> _options;

    public OptionsController(IOptions<WebsiteProfileOptions> options)
    {
        _options = options;
    }

    // Get api/options
    [HttpGet]
    public WebsiteProfileOptions Get()
    {
        return _options.Value;
    }
}

😎 You don't have to register Options in the DI container, it will be automatically registered by ASP.NET Core.

We can use Primary Constructor to inject the configuration.

[Route("api/[controller]")]
[ApiController]
public class OptionsController(IOptions<WebsiteProfileOptions> _options) : ControllerBase
{
    // this setting is optional, if you want to make _options readonly
    readonly IOptions<WebsiteProfileOptions> _options = _options;

    // Get api/options
    [HttpGet]
    public WebsiteProfileOptions Get()
    {
        return _options.Value;
    }
}

We can change the configuration in the Program.cs file.

builder.Services.AddControllers();
builder.Services.Configure<WebsiteProfileOptions>(options =>
{
    options.ThemeColor = "blue";
    options.FontSize = "20px";
});

var app = builder.Build();

But best practice is to setting the configuration in appsettings.json file.

builder.Services.Configure<WebsiteProfileOptions>(builder.Configuration);
var app = builder.Build();
{
  "WebsiteProfileOptions": {
    "ThemeColor": "blue",
    "FontSize": "18px"
  }
}

Nested configuration in the appsettings.json file.

builder.Services.Configure<WebsiteProfileOptions>(builder.Configuration.GetSection("WebsiteProfile"));
{
  "WebsiteProfile": {
    "ThemeColor": "yellow",
    "FontSize": "36px"
  }
}

We can use IOptionsSnapshot to get the fresh configuration per request.

[Route("api/[controller]")]
[ApiController]
public class OptionsController(IOptionsSnapshot<WebsiteProfileOptions> _options) : ControllerBase
{
    // Get api/options
    [HttpGet]
    public WebsiteProfileOptions Get()
    {
        return _options.Value;
    }
}

IOptions: Singleton, static configuration values.
IOptionsSnapshot: Scoped, fresh configuration per request.
IOptionsMonitor: Singleton, dynamic configuration with change notifications.


We can use AddJsonFile to add custome configuration file.

builder.Configuration.AddJsonFile("profile.json", optional: true, reloadOnChange: true);
builder.Services.Configure<WebsiteProfileOptions>(builder.Configuration.GetSection("WebsiteProfile"));

profile.json

{
  "WebsiteProfile": {
    "ThemeColor": "red",
    "FontSize": "24px"
  }
}

If we add multiple configuration files, the last one will override the previous one.

builder.Configuration.AddJsonFile("profile.json", optional: true, reloadOnChange: true);
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
    ["WebsiteProfile:ThemeColor"] = "Red",
    ["WebsiteProfile:FontSize"] = "1px"
});
builder.Services.Configure<WebsiteProfileOptions>(builder.Configuration.GetSection("WebsiteProfile"));

We can use builder.Configuration as IConfigurationRoot to get the configuration order.

foreach (var provider in (builder.Configuration as IConfigurationRoot).Providers)
{
    Console.WriteLine(provider.ToString());
}
MemoryConfigurationProvider
EnvironmentVariablesConfigurationProvider Prefix: 'ASPNETCORE_'
MemoryConfigurationProvider
EnvironmentVariablesConfigurationProvider Prefix: 'DOTNET_'
JsonConfigurationProvider for 'appsettings.json' (Optional)
JsonConfigurationProvider for 'appsettings.Development.json' (Optional)
EnvironmentVariablesConfigurationProvider
Microsoft.Extensions.Configuration.ChainedConfigurationProvider
JsonConfigurationProvider for 'profile.json' (Optional)
MemoryConfigurationProvider

We can also add XML and INI configuration files.

profile.xml

<WebsiteProfile>
  <ThemeColor>blue</ThemeColor>
  <FontSize>18px</FontSize>
[WebsiteProfile]
ThemeColor=green
FontSize=16px
builder.Configuration.AddXmlFile("profile.xml", optional: true, reloadOnChange: true);
builder.Configuration.AddIniFile("profile.ini", optional: true, reloadOnChange: true);

Get Configuration by key without Model to bind

We can use IConfiguration to get the configuration by key, instead of using IOptions to skip Model binding 😉

[Route("api/[controller]")]
[ApiController]
public class ConfigController(IConfiguration _configuration) : ControllerBase
{
    [HttpGet]
    public string? Get() => _configuration["WebsiteProfile:ThemeColor"];
}

Using Bind to bind the configuration

[HttpGet("Bind")]
public WebsiteProfileOptions Bind()
{
    WebsiteProfileOptions options = new ();
    _configuration.Bind("WebsiteProfile", options);
    return options.options;
}

⭐ Using IConfiguration, it's useful when you need to access a single or a few configuration values directly and you don't want to create a class to represent those settings, otherwise, use IOptions to bind the configuration to a class.

Multiple Configuration Files

Named options in .NET allow you to configure and manage multiple instances of options with different settings. This feature is particularly useful when you need to inject different configurations into various parts of your application.

⚡ If we want to use multiple configuration files, we can use NamedOptions to bind the configuration.

builder.Configuration.AddJsonFile("configSource1.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile("configSource2.json", optional: false, reloadOnChange: true);

builder.Services.Configure<ConfigSettings>("Source1", builder.Configuration.GetSection("CommonSettings"));
builder.Services.Configure<ConfigSettings>("Source2", builder.Configuration.GetSection("CommonSettings"));

var app = builder.Build();
public class ConfigSettings
{
    public string Setting { get; set; }
}
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly ConfigSettings _source1Config;
    private readonly ConfigSettings _source2Config;

    public MyController(IOptionsSnapshot<ConfigSettings> source1ConfigOptions, IOptionsSnapshot<ConfigSettings> source2ConfigOptions)
    {
        _source1Config = source1ConfigOptions.Get("Source1");
        _source2Config = source2ConfigOptions.Get("Source2");
    }

    [HttpGet("source1")]
    public IActionResult GetSource1Config()
    {
        return Ok(_source1Config);
    }

    [HttpGet("source2")]
    public IActionResult GetSource2Config()
    {
        return Ok(_source2Config);
    }
}
// configSource1.json
{
  "CommonSettings": {
    "Setting": "Value in source 1 🦊"
  }
}

//configSource2.json
{
  "CommonSettings": {
    "Setting": "Value in source 2 🐸"
  }
}