क्रिश्चियन शॉ
  • होम
  • Blog
    • प्रोग्रामिंग
      • C#
      • पॉवरशेल
      • Python
      • SQL
    • वर्डप्रेस
      • ट्युटोरियल
    • क्लाउड
    • होम ऑटोमेशन
      • होम असिस्टेंट
        • Node-Red
    • Career
  • सेवाएं
  • शब्दकोष
  • About
No Result
View All Result
क्रिश्चियन शॉ
  • होम
  • Blog
    • प्रोग्रामिंग
      • C#
      • पॉवरशेल
      • Python
      • SQL
    • वर्डप्रेस
      • ट्युटोरियल
    • क्लाउड
    • होम ऑटोमेशन
      • होम असिस्टेंट
        • Node-Red
    • Career
  • सेवाएं
  • शब्दकोष
  • About
No Result
View All Result
क्रिश्चियन शॉ
No Result
View All Result
Home प्रोग्रामिंग C#
ioptions

How to use IOptions Pattern to get configuration from json files in .NET C#

by ईसाई
2022-20-मई
in C#
0

Working with IOptions<> in .NET for configuration of the application. In almost any project or application you will have some kind of settings that would need to be configured and often changed depending on the environment you are running the application within. This could be user secrets, default settings, paths, etc…A very classic example is connection strings for our databases – we don’t want to share them.

Luckily for the developers using .NET or ASP.NET Core the configuration part has been extended and enhanced over the past couple of years. We can now store settings in environment variables, user secrets, appsettings.json or custom settings files or even a database. In .NET we can use strongly typed settings using the IOptions<> pattern.

I was working on a big API project the other day and was trying to bind several custom configuration classes, but it wouldn’t bind to my properties in the json files. The reason was because I forgot to register each configuration file at startup.

If you are ready to get started learning how to utilize the power of the IOptions<> pattern, then let’s get started.

विषय-सूची
  1. An introduction to strongly typed configurations
  2. How to bind configuration to classes using IOptions
    • Add configuration files to the host builder
    • Bind configurations values to class
  3. Use Configuration Values from IOptions in a class or controller
  4. Summary
  5. Repository

An introduction to strongly typed configurations

When using .NET you don’t have a default way to retrieve AppSettings["Settings"]. When reading through the documentation at Microsoft, the recommended approach is to create a strongly typed configuration class(es) that matches sections of your configuration files.

public class SwaggerSettings
{
    public string Title { get; set; }
    public string Version { get; set; }
    public string Description { get; set; }
    public string TermsOfserviceLink { get; set; }
}

That would map to this inside swagger.json (a file we were create).

{
  "SwaggerSettngs": {
    "Title": "Demo API with IOptions",
    "Version": "v1",
    "Description": "This is a description loaded from the configuration using IOptions",
    "TermsOfserviceLink": "https://christian-schou.dk/"
  }
}

As you can see the class SwaggerSettings completely matches the configuration file. This makes it possible to set the properties in the configuration file in the strongly types configuration class during runtime.

How to bind configuration to classes using IOptions

To get started, you have to create a new project built on the ASP.NET Core Web API Template in Visual Studio. I have named mine IOptionsConfigurationDemo, but you can name yours as you want, or simply add the code inside your current application.

For this project, we will create some properties to be loaded in Swagger, giving us a dynamic way to change the texts in Swagger depending on the environment we run the application within. I personally like to add a text in my title in Swagger indicating that I’m working on the development, staging, testing, etc… environment. This makes everything a bit more clear and avoids me from making accidentally mistakes.

First thing I did was creating a new folder named Configurations within my root project. Inside that folder I have created two configuration new files named swagger.json and swagger.Development.json. I have also added a file named Startup.cs. Both configration files contain the following json code:

{
  "SwaggerSettings": {
    "Enable": true,
    "Title": "IOptions Demo PROD",
    "Version": "v1",
    "Description": "A demo of how you can use IOptions to bind value of properties to strongly types classes in .NET",
    "ContactName": "Christian Schou",
    "ContactEmail": "[email protected]",
    "ContactUrl": "https://christian-schou.dk",
    "License": true,
    "LicenseName": "MIT License",
    "LicenseUrl": "https://christian-schou.dk"
  }
}
{
  "SwaggerSettings": {
    "Enable": true,
    "Title": "IOptions Demo DEV",
    "Version": "v1",
    "Description": "A demo of how you can use IOptions to bind value of properties to strongly types classes in .NET",
    "ContactName": "Christian Schou",
    "ContactEmail": "[email protected]",
    "ContactUrl": "https://christian-schou.dk",
    "License": true,
    "LicenseName": "MIT License",
    "LicenseUrl": "https://christian-schou.dk"
  }
}

In order to be sure these configuration files are loaded and bound to my SwaggerSettings class, we have to do two things.

  1. Setup the ConfigurationBuilder to load the files at runtime.
  2. Bind our SwaggerSettings class to a configuration section.

By default this .NET template will contain logic for configuring OpenAPI/Swagger in Program.cs. Also ConfigurationBuilder has already been configured to load default settings in appsettings.json from our environment variables.

Add configuration files to the host builder

Let’s add our own configuration extension to the host part of Program.cs. Inside Configurations we created a file named Startup.cs, let’s add some code in that file. Remember to change the namespace, if you are copying the code directly.

namespace IOptionsConfigurationDemo.Configurations
{
    internal static class Startup
    {
        internal static ConfigureHostBuilder AddConfigurations(this ConfigureHostBuilder host)
        {
            host.ConfigureAppConfiguration((context, config) =>
            {
                const string configurationsDirectory = "Configurations"; // Path for pickup location of configuration files
                var env = context.HostingEnvironment; // Get current hosting environment

                // Application Specific Configurations
                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"{configurationsDirectory}/swagger.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"{configurationsDirectory}/swagger.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                    .AddEnvironmentVariables();
            });
            return host;
        }
    }
}

A brief explanation of the code above.

  • First we create an extension of ConfigureHostBuilder and then we setup a remainder of the build process and application using ConfigureAppConfiguration.
  • We then load the configurations path and the current environment into a variable.
  • Then we add each configuration file with it’s path and the options for it to load the configuration file in a specific environment defined by the env variable.
  • Finally we add the environment variables to host and return host to the requesting method.

Now we have to register the extension in Program.cs. This is done like below at line 7:

using IOptionsConfigurationDemo.Configurations;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Host.AddConfigurations();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Bind configurations values to class

Now we have to bind the values of our configuration files to the class. Let’s create a new folder at root level named Settings and add a new class inside this folder named SwaggerSettings.cs. This is a mirror of the properties we added earlier in swagger.json just as a strongly typed class.

namespace IOptionsConfigurationDemo.Settings
{
    public class SwaggerSettings
    {
        public bool Enable { get; set; }
        public string? Title { get; set; }
        public string? Version { get; set; }
        public string? Description { get; set; }
        public string? ContactName { get; set; }
        public string? ContactEmail { get; set; }
        public string? ContactUrl { get; set; }
        public bool License { get; set; }
        public string? LicenseName { get; set; }
        public string? LicenseUrl { get; set; }
    }
}

Open the Startup class we created in Configurations again and add the following code to it (line 24-28):

using IOptionsConfigurationDemo.Settings;

namespace IOptionsConfigurationDemo.Configurations
{
    internal static class Startup
    {
        internal static ConfigureHostBuilder AddConfigurations(this ConfigureHostBuilder host)
        {
            host.ConfigureAppConfiguration((context, config) =>
            {
                const string configurationsDirectory = "Configurations"; // Path for pickup location of configuration files
                var env = context.HostingEnvironment; // Get current hosting environment

                // Application Specific Configurations
                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"{configurationsDirectory}/swagger.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"{configurationsDirectory}/swagger.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                    .AddEnvironmentVariables();
            });
            return host;
        }

        internal static IServiceCollection AddConfigurationServices(this IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<SwaggerSettings>(options => configuration.GetSection("SwaggerSettings").Bind(options));
            return services;
        }
    }
}

What did we just do?

  • We start off by creating a new extension of IServiceCollection named AddConfigurationServices that takes IServiceCollection and IConfiguration as parameters.
  • At line 26 we register a new action to configure an option of type SwaggerSettings. We then instruct the Configure() method to load the section named SwaggerSettings and bind the options (configurations) to that class.

Finally we have to make sure that this extension is also loaded at startup in Program.cs. This has to be done after we have added the configurations and loaded them. Go to Program.cs and add the code at line 8.

using IOptionsConfigurationDemo.Configurations;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Host.AddConfigurations();
builder.Services.AddConfigurationServices(builder.Configuration);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

That’s it, now the properties in swagger.json will be bound to the strongly typed class at runtime. Now we only have to use the settings to configure Swagger/OpenAPI. For this I have created a new folder named OpenAPI and added a new class file named Startup.cs.

Inside this new Startup.cs file we have to add a new variable that holds the values of our swagger settings. Because Swagger currently is registered in Program.cs and I want to keep my Program.cs file clean, we have to create an extension method that can be added to Program.cs that will contain all logic for adding Swagger to our application.

First we have to install a new packag named NSwag.AspNetCore in our project. You can do that by using below commands or by searching it up in the NuGet Package Manager.

# Package Manager
Install-Package NSwag.AspNetCore

# .NET CLI
dotnet add package NSwag.AspNetCore

#PackageReference
<PackageReference Include="NSwag.AspNetCore" Version="VERSION-HERE" />

With that out of the picture, let’s create a new extension method to configure swagger with our new strongly types configuration class.

using IOptionsConfigurationDemo.Settings;

namespace IOptionsConfigurationDemo.OpenAPI
{
    public static class Startup
    {
        internal static IServiceCollection AddOpenApiDocumentation(this IServiceCollection services, IConfiguration configuration)
        {
            SwaggerSettings? settings = configuration.GetSection(nameof(SwaggerSettings)).Get<SwaggerSettings>();

            if (settings.Enable)
            {
                services.AddEndpointsApiExplorer();
                services.AddOpenApiDocument((document, serviceProvider) =>
                {
                    document.PostProcess = doc =>
                    {
                        doc.Info.Title = settings.Title;
                        doc.Info.Version = settings.Version;
                        doc.Info.Description = settings.Description;
                        doc.Info.Contact = new()
                        {
                            Name = settings.ContactName,
                            Email = settings.ContactEmail,
                            Url = settings.ContactUrl
                        };
                        doc.Info.License = new()
                        {
                            Name = settings.LicenseName,
                            Url = settings.LicenseUrl
                        };
                    };
                });
                
            }

            return services;
        }
    }
}
  • First we create an extension method of IServiceCollection named AddOpenApiDocumentation with two parameters for the service and configuration.
  • Next we get the section of our configuration named the same as our settings class and load the settings into a variable of that model.
  • Finally we configure OpenAPI using the settings making the OpenApiDocument dynamic depending on the environment that has been chosen.

Now we have to register this extension service in our Program.cs file. This has to be done after the configuration has been done in the pipeline. Please update the whole Program.cs file as I have done some housekeeping to remove the old Swagger implementation.

using IOptionsConfigurationDemo.Configurations;
using IOptionsConfigurationDemo.OpenAPI;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Host.AddConfigurations();
builder.Services.AddConfigurationServices(builder.Configuration);
builder.Services.AddControllers();
// Add OpenAPI
builder.Services.AddOpenApiDocumentation(builder.Configuration);

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

If I were to launch the application now I would get a problem with the OpenAPI interface, as I have not told the pipeline to use OpenAPI and SwaggerUi3. Let’s go ahead and do just that. Inside the Startup.cs with OpenAPI add the following new extension, just below our previous service extension AddOpenApiDocumentation:

internal static IApplicationBuilder UseOpenApiDocumentation(this IApplicationBuilder app, IConfiguration configuration)
        {
            SwaggerSettings? settings = configuration.GetSection(nameof(SwaggerSettings)).Get<SwaggerSettings>();

            if (settings.Enable)
            {
                app.UseOpenApi();
                app.UseSwaggerUi3(options =>
                {
                    options.DefaultModelExpandDepth = -1; // Don't expand the schemas and endpoints
                    options.DocExpansion = "none";
                    options.TagsSorter = "alpha";
                });
            }

            return app;
        }

This one also has to be registered in Program.cs, but after the services are in place. Check line 18:

using IOptionsConfigurationDemo.Configurations;
using IOptionsConfigurationDemo.OpenAPI;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Host.AddConfigurations();
builder.Services.AddConfigurationServices(builder.Configuration);
builder.Services.AddControllers();
builder.Services.AddOpenApiDocumentation(builder.Configuration); // Add OpenAPI

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.UseOpenApiDocumentation(builder.Configuration);

app.MapControllers();

app.Run();

Let’s take it for a ride and see the result:

IOptions, Swagger

Perfect! We now see the DEV in the title just as expected. Let’s try and switch environment to production and see what happens.

IOptions, Swagger

Awesome! Now we can change the value of our application dynamically just by having multiple config files. This makes the life easier for us when deploying to different environments like testing, pre-prod and prod.

Use Configuration Values from IOptions in a class or controller

If you want to, you could also do dependency injection to gain access to the settings values.

public class SomeController : ControllerBase
{
    private SomeSettings _settings;
    public SomeController(IOptions<SomeSettings> settings)
    {
        _settings = settings.Value
    }

    public string GetSettingsString()
    {
        return _settings.SomeString;
    }
}

That’s how easy it is to get access to the settings once the initial work has been completed in Program.cs.

Summary

Using the strongly typed configuration in .NET is a great way to work with settings. IOptions help us provide our application with a clean way to apply the Interface Segregation Principle to our configurations. When the configurations have been added one time in the service extensions you can use them throughout the whole application.

I personally use this way when I add configuration files to my applications and the settings are not stored in a database. If you got any issues, questions or suggestions, please let me know in the comments below. Happy coding!

Repository

If you would like to have a look at the solution, you can check out the repository at my Github here: https://github.com/Christian-Schou/IOptionsConfigurationDemo

Tags: .NET 6.Net CoreAPIASP.NET CoreC#ConfigurationsDevelopment
Previous Post

How to use XML with C# – A beginners guide for basic XML operations in .NET

Next Post

How to implement Rate Limiting in an ASP.NET Core Web API

ईसाई

ईसाई

Hello 👋 My name is Christian and I am 26 years old. I'm an educated Software Developer with a primary focus on C#, .NET Core, Python, and PowerShell. Currently, I'm expanding my skills in Software Robots and Cloud Architecture. In some of my spare time, I share my knowledge about tech stuff on my blog.

Related Posts

watchdog
ASP.NET Core

The #1 guide to show real-time .NET 6 logs for Web Apps and APIs in a modern way using WatchDog for Free

by ईसाई
2022-13-अगस्त
0

A reader recently asked me for a more modern way to view log files for requests and exceptions in a...

Read more
restful web api

How to build a RESTful Web API using ASP.NET Core and Entity Framework Core (.NET 6)

2022-25-जुलाई
dynamically register entities

How to Dynamically Register Entities in DbContext by Extending ModelBuilder?

2022-23-जुलाई
Dockerize ASP.NET Core

How to Compose an ASP.NET Core Web API (.NET 6) with an MS SQL Server 2022 on Linux in Docker

2022-19-जुलाई
pattern matching in switch

How to do pattern matching in switch statements – C# version >= 7.0

2022-11-जुलाई
Next Post
rate limiting, rate limit

How to implement Rate Limiting in an ASP.NET Core Web API

प्रातिक्रिया दे जवाब रद्द करें

आपका ईमेल पता प्रकाशित नहीं किया जाएगा. आवश्यक फ़ील्ड चिह्नित हैं *

क्रिश्चियन शॉ

क्रिश्चियन शॉ

Software Developer

Hello - my name is Christian and I am 26 years old. I'm an educated Software Developer with a primary focus on C#, .NET Core, Python, and PowerShell. Currently, I'm expanding my skills in Software Robots and Cloud Architecture. In some of my spare time, I share my knowledge about tech stuff on my blog.

Recent articles

personal website
Career

Top 6 things to add on your personal website to get hired for a tech job

by ईसाई
2022-7-अगस्त
0

Back in the days before the internet was a thing like it is today, we used to have business cards...

Read more
watchdog

The #1 guide to show real-time .NET 6 logs for Web Apps and APIs in a modern way using WatchDog for Free

2022-13-अगस्त
get hired for a tech job

5 tips to help you get hired for a tech job

2022-31-जुलाई
restful web api

How to build a RESTful Web API using ASP.NET Core and Entity Framework Core (.NET 6)

2022-25-जुलाई
dynamically register entities

How to Dynamically Register Entities in DbContext by Extending ModelBuilder?

2022-23-जुलाई

क्रिश्चियन शॉ

Software Developer

Hello - my name is Christian and I am 26 years old. I'm an educated Software Developer with a primary focus on C#, .NET Core, Python, and PowerShell. Currently, I'm expanding my skills in Software Robots and Cloud Architecture. In some of my spare time, I share my knowledge about tech stuff on my blog.

Recent articles

personal website

Top 6 things to add on your personal website to get hired for a tech job

2022-7-अगस्त
watchdog

The #1 guide to show real-time .NET 6 logs for Web Apps and APIs in a modern way using WatchDog for Free

2022-13-अगस्त
get hired for a tech job

5 tips to help you get hired for a tech job

2022-31-जुलाई
  • hi_INहिन्दी
    • da_DKDansk
    • en_USEnglish
    • de_DEDeutsch
    • pt_BRPortuguês do Brasil
  • Contact
  • गोपनीयता नीति
  • सेवा की शर्तें

© 2022 क्रिश्चियन शॉ - All rights reserved.

No Result
View All Result
  • होम
  • Blog
    • प्रोग्रामिंग
      • C#
      • पॉवरशेल
      • Python
      • SQL
    • वर्डप्रेस
      • ट्युटोरियल
    • क्लाउड
    • होम ऑटोमेशन
      • होम असिस्टेंट
    • Career
  • सेवाएं
  • शब्दकोष
  • About

© 2022 क्रिश्चियन शॉ - All rights reserved.

मैं आपकी वरीयताओं को याद करके और बार-बार आने वाली यात्राओं को याद करके आपको सबसे अधिक प्रासंगिक अनुभव देने के लिए अपनी वेबसाइट पर कुकीज़ का उपयोग करता हूं। “स्वीकार करें” पर क्लिक करके, आप सभी कुकीज़ के उपयोग के लिए सहमति देते हैं।
मेरी निजी जानकारी न बेचें.
कुकी सेटिंगACCEPT
गोपनीयता और कुकीज़ नीति

गोपनीयता अवलोकन

This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
CookieDurationDescription
__gads1 year 24 daysThe __gads cookie, set by Google, is stored under DoubleClick domain and tracks the number of times users see an advert, measures the success of the campaign and calculates its revenue. This cookie can only be read from the domain they are set on and will not track any data while browsing through other sites.
_ga2 yearsThe _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors.
_ga_0J2F6JVWSD2 yearsThis cookie is installed by Google Analytics.
_gat_gtag_UA_84232734_11 minuteSet by Google to distinguish users.
_gid1 dayInstalled by Google Analytics, _gid cookie stores information on how visitors use a website, while also creating an analytics report of the website's performance. Some of the data that are collected include the number of visitors, their source, and the pages they visit anonymously.
YouTube2 yearsYouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data. I embed YouTube videos in my articles/tutorials - you won't get the full experience of the articles if this is deactivated.
Advertisement
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
CookieDurationDescription
IDE1 year 24 daysGoogle DoubleClick IDE cookies are used to store information about how the user uses the website to present them with relevant ads and according to the user profile.
test_cookie15 minutesThe test_cookie is set by doubleclick.net and is used to determine if the user's browser supports cookies.
VISITOR_INFO1_LIVE5 months 27 daysA cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.
YSCsessionYSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.
yt-remote-connected-devicesneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
yt-remote-device-idneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT
Powered by CookieYes Logo