Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Jul 11

.NET | Working with options pattern

.NET | Working with options pattern
Автор:
Источник:
Просмотров:
1933

Another way to read data from the configuration data

Options pattern introduction

Options pattern is a flexible configuration data management way that enables us to use strongly typed configurations. It uses classes to bind groups of related configurations. We can inject these classes via dependency injection with IOptions<T> to include only the part that we need.

This pattern satisfies two main and important software engineering principles.

  • Encapsulation: these classes only depend on the configuration settings that they use.
  • Separation of Concerns: Settings for different parts of the app aren’t depending on one another.

Why use IOptions pattern

For example, let’s assume that we have some configuration settings in the appSettings.json

{
  ...

  "Jwt": {
    "Issuer": "thisisyourissuers",
    "SigningKey": "thiskeyisveryhardtobreak",
    "IsValidateLifetime": true
  },

  ...
}

For reading the Issuer, we can directly access its value by using the classic IConfiguration instance, which is already automatically registered into the dependency injection container since ASP.NET Core 2.0.

We can directly use it through:

public class HomeController
{
   public HomeController(IConfiguration configuration)
   {
      // Use IConfiguration instance
      var issuer = configuration["Jwt:Issuer"]   }
}

This way will become extremely hard to maintain when the structure of the configuration goes diverse with deeply nested sections.

Instead, we can create a “strongly-typed” class to sections in the configuration to separate the concerns and make it easy to access and maintain.

Back in our example, our strongly typed class for the Jwt section can be as below:

public class JwtOptions
{
  public string Issue {get; set;}
  public string SigningKey {get; set;}
  public bool IsValidateLifetime {get; set;}
}

We need to ensure that the property names and types must match the key names and values exactly in the configuration file.

The next step is to register this class into the dependency injection container so that we can use it in other areas, like services or controllers.

One approach is to use the bind method. We can manually create an instance of this class and bind it to the configuration section of the JSON file.

# Startup.ConfigureServices() Method #

JwtOptions jwtOptions = new();
Configuration.GetSection("Jwt").Bind(jwtOptions);
services.AddSingleton(jwtOptions);

Alternatively, we can use them Get() to avoid using the “new” keyword.

JwtOptions jwtOptions = Configuration.GetSection("Jwt").Get<JwtOptions>();
services.AddSingleton(mailFeature);

To use the instance in our logic, we can just inject the class into the constructor of the component where it will be used.

public class HomeController : ControllerBase
{
    private readonly JwtOptions jwtOptions;

    public JwtController(JwtOptions jwtOptions)
    {
        this.jwtOptions = jwtOptions;
    }

Although this approach works, the drawback is also obvious that we still have to explicitly create an instance by ourselves while using the dependency injection.

Here is where the IOptions pattern comes into play.

How to use IOptions pattern

The above logic can be written as:

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

        services.Configure<JwtOptions>(Configuration.GetSection(nameof(JwtOptions)));

        // Some other code....
    }
}

Instead of using Bind or Get methods, we can achieve this by using GetSection to get a configuration sub-section with the specified key.

In this way, we can easily achieve the same result by writing easier and less code.

The IOptions interface is part of the Microsoft.Extensions.Options namespace, which is available implicitly in the .NET Core package.

To get the TOptions instance in our controller or services, there are three interfaces provided by this package.

  • IOptions
  • IOptionsSnapshot
  • IOptionsMonitor

They can be used in the same way but for different scenarios, like:

public class JwtController : ControllerBase
{
    private readonly JwtOptions jwtOptions;

    public JwtController(IOptions<JwtOption> jwtOptions)
    {
        this.jwt = jwtOptions.Value;
    }
}

When to use which

IOptions:

  • registered as a singleton service, hence can be injected into any service.
  • configuration changes cannot be re-read once instantiated, since it’s a singleton.
  • Doesn’t support “named” options.

IOptionsSnapshot:

  • registered a scoped service, hence cannot be used inside the singleton service.
  • enable reload when configuration changed.
  • Supports “named” options.

IOptionsMonitor:

  • registered as a singleton service, hence can be injected into any service.
  • enable reload when configuration changed.
  • Supports “named” options.

Check here for understanding what is named option:

Похожее
Apr 29
Author: Salman Karim
Background tasks are crucial in many software applications, and scheduling them to run periodically or at specific times is a common requirement. In the .NET ecosystem, Quartz.NET provides a robust and flexible framework for scheduling such tasks. Let’s delve into...
Nov 30, 2023
Author: Dev·edium
Keeping your C# applications safe and sound. Keeping app secrets safe is always tricky for developers. We want to work on the main parts of the app without getting distracted by secret-keeping. But, the app’s safety is very important. So,...
Jun 27
Author: Dayanand Thombare
Introduction Caching is a technique used to store frequently accessed data in a fast-access storage layer to improve application performance and reduce the load on backend systems. By serving data from the cache, we can avoid expensive database queries or...
Nov 22, 2023
Author: Arnold Abraham
There is a simple solution just around the corner Null as a return value is so easy to implement, but it brings many problems. So people make mistakes. That is part of being human. Sometimes it turns out to be...
Написать сообщение
Тип
Почта
Имя
*Сообщение
RSS
Если вам понравился этот сайт и вы хотите меня поддержать, вы можете
Почему вы никогда не должны соглашаться на собеседования с программированием
«Великое увольнение» продолжается: теперь с работы уходят даже боссы
Soft skills: 18 самых важных навыков, которыми должен владеть каждый работник
5 приемов увеличения продуктивности разработчика
Зарплата по результатам собеседования — лучший способ сократить отклики на вакансию, а тестовые задания — избыточны
9 тяжёлых уроков, которые я усвоил за 18 лет разработки
8 вещей, которых не должен бояться разработчик
Рассуждение на тему, какую базу данных выбирать
Scrum: 12 терминов, которые нужно запомнить
Hangfire — планировщик задач для .NET
LinkedIn: Sergey Drozdov
Boosty
Donate to support the project
GitHub account
GitHub profile