Introduction
In this article, we will see another approach to read configuration settings using the Named Options feature in an ASP.NET core project.
Source Code
You can get the source code of this article from
GitHub.
Named Options
Named Options are useful when you have two or more setting sections in a configuration file with common properties. In this case, you do not need to create separate options classes for each section and register them separately into the ASP.NET Dependency Injection container. Let's see an example of common settings:
appsettings.json
- "DashboardThemeSettings": {
- "NormalTheme": {
- "CssUrl": "https://mysite.com/CSS/theme/normal/main.css",
- "LogoName": "normal-theme-logo.png"
- },
- "DarkTheme": {
- "CssUrl": "https://mysite.com/CSS/theme/dark/main.css",
- "LogoName": "dark-theme-logo.png"
- }
- }
Suppose you have two different CSS and images URLs provided by design team for applying two different themes on your application dashboard like above. Now on application initialization, you want to read settings from configurations and apply them whenever it is required to change.
Maybe this example is not suitable for you, but my goal is just to describe here only how we can achieve this type of situation using Named Options pattern with an easier approach and minimal code.
However, we can also achieve this functionality using the Options Pattern as described in the
previous article. let's recap Options Pattern quickly with this article also so that you could understand the real need of the Named Options approach.
Using Options Pattern
You will have to create two different strongly types of option classes while using this pattern and register them separately into DI containers. You would inject them all to use inside any service or controller. For example:
Creating strongly types options classes
- public class NormalThemeDashboardSettings
- {
- public string CssUrl { get; set; }
- public string LogoName { get; set; }
- }
-
- public class DarkThemeDashboardSettings
- {
- public string CssUrl { get; set; }
- public string LogoName { get; set; }
- }
Registering into DI container
You would register all options classes into DI container inside ConfigureServices method in Startup.cs file like below:
- public void ConfigureServices(IServiceCollection services)
- {
- ... //other code
-
- services.Configure<NormalThemeDashboardSettings>(_configuration.GetSection("DashboardThemeSettings:NormalTheme"));
- services.Configure<NormalThemeDashboardSettings>(_configuration.GetSection("DashboardThemeSettings:DarkTheme"));
- }
Accessing configurations
To access and use settings inside any service or controller, you would inject them into constructor with the help of either IOptions or IOptionsSnapshot, like below:
- public class ThemeConfigurationReader : IThemeConfigurationReader
- {
- NormalThemeDashboardSettings normalThemeSettings;
- DarkThemeDashboardSettings darkThemeSettings;
-
- public ThemeConfigurationReader(
- IOptions<NormalThemeDashboardSettings> normalThemeOptions,
- IOptions<DarkThemeDashboardSettings> darkThemeOptions)
- {
- normalThemeSettings = normalThemeOptions.Value;
- darkThemeSettings = darkThemeOptions.Value;
- }
-
- public string ReadThemeSettings()
- {
- return JsonConvert.SerializeObject(new
- {
- this.normalThemeSettings,
- this.darkThemeSettings
- });
- }
- }
Output
Like above, we can achieve settings by making separate strongly typed classes. The downside of this approach is that if any other theme settings will be added to the configuration file in the future, then you will have to create one more strongly typed option class and register it in DI container as done above.
Now let's see how we can achieve this using Named Options.
Using Named Options
Registering Named Options is very simple. Using this pattern, we only need to create one strongly type option class and use them for all those configurations, which have the same properties. This lets us access them by name when we need to use them inside any service or controller.
Creating a single Options class
We need to create only one strongly typed option class as below:
- public class DashboardThemeSettings
- {
- public string CssUrl { get; set; }
- public string LogoName { get; set; }
- }
Registering into DI container
Since we are using the Named Options approach, we need to register this single options class using an overload version of the Configure method of IServiceCollection where we need to provide a unique name as the first parameter. This name allows us to retrieve the specific configuration from our consuming services as shown below in below snapshot:
Startup.cs
- public void ConfigureServices(IServiceCollection services)
- {
- ... //other code
-
- //services.Configure<NormalThemeDashboardSettings>(_configuration.GetSection("DashboardThemeSettings:NormalTheme"));
- //services.Configure<DarkThemeDashboardSettings>(_configuration.GetSection("DashboardThemeSettings:DarkTheme"));
-
- services.AddSingleton<IThemeConfigurationReader, ThemeConfigurationReader>();
-
- services.Configure<DashboardThemeSettings>("Normal", _configuration.GetSection("DashboardThemeSettings:NormalTheme"));
- services.Configure<DashboardThemeSettings>("Dark", _configuration.GetSection("DashboardThemeSettings:DarkTheme"));
- }
Consuming named options classes into services
Please note this important statement - Named Options feature is not supported by the IOptions interface. To consume Named Options, either IOptionsSnapshot or IOptionsMonitor is used.
As we saw in our
previous article, using Options Pattern, IOptionsSnapshot can not be consumed inside a service registered as a singleton lifetime scope. because this is not allowed by the safety feature of ASP.NET Dependency Injection Container called "Scoped Validation".
Since we have registered our "ThemeConfigurationReader"service as a singleton lifetime, IOptionsSnapshot won't work in this case. We will use IOptionsMonitor to consume settings.
- public class ThemeConfigurationReader : IThemeConfigurationReader
- {
- private readonly DashboardThemeSettings _noralThemeSettings;
- private readonly DashboardThemeSettings _darkThemeSettings;
-
- public ThemeConfigurationReader(IOptionsMonitor<DashboardThemeSettings> optionsMonitor)
- {
- _noralThemeSettings = optionsMonitor.Get("Normal");
- _darkThemeSettings = optionsMonitor.Get("Dark");
- }
-
- public string ReadThemeSettings()
- {
- return JsonConvert.SerializeObject(new
- {
- this._noralThemeSettings,
- this._darkThemeSettings
- });
- }
- }
As per above code, we need to provide the same name (we used while registering named options inside ConfigureServices services method) inside Get method for accessing individual configuration. Now run the application to see the output:
Output
Choose the options interface while using Named Options/Options Pattern in ASP.NET Core
Advantage of using Named Options approach
The biggest advantage of using the Named Options feature is that you do not need to create any new option class to add new theme settings in the case that in the future there are other theme settings added to the configuration. You just need to configure a new named "DashboardThemeSettings" options object.
Summary
Through this article, we learned a very useful feature Named Options for accessing configurations in our ASP.NET Core project.
- How to use Named Options to access the same properties configurations in an ASP.NET project?
- How to use multiple instances of same properties settings inside appsettings.json using single strongly-typed options class with named options?
- What is the advantage of using Named Options over Options Pattern?
- Which option interface should be used while using Named Options or Options Pattern in an ASP.NET Core project?
Thanks a lot for reading. I hope you'll love this article. Please share your valuable suggestions and feedback. Post in the comment box in case you have any questions.
Have a good day!