You are currently viewing Utilising the .Net Options Pattern to Enhance Configuration Management

Utilising the .Net Options Pattern to Enhance Configuration Management

In our continuous effort to improve our development processes, we recently conducted a comprehensive review of all existing code repositories and projects within our team. The goal was to compile a technical debt register, and during this process, we discovered a common practice: most applications relied on environment variables for their settings and configurations.

The Current Scenario

Typically, our projects used environment variables like this to manage the settings for services such as email notifications:

While this method is functional, it poses challenges, especially when writing unit tests where settings need to be injected using an appSettings.json file. A better approach is needed for injecting the appropriate settings directly into service classes.

Introducing the .Net Options Pattern

The .Net Options Pattern offers a robust solution by allowing configuration settings to be bound to POCOs (Plain Old CLR Objects) and injected into service classes using standard .Net dependency injection. This method not only simplifies the configuration structure but also enhances code maintainability and testability.

Simple Configuration Example

Let’s start by defining a new class to hold settings for an email service and show how it is injected into the EmailService class:

Now you can store these configuration values in the appSettings.json or a local.settings.json file.

Finally, lets look at how we register this configuration class in the dependency injection:

Classes and Inheritance

Let’s extend our example by adding an FTP service that shares some settings with the email service. We refactor the settings into reusable classes:

public class ServiceHost

{

   public string Host { get; set; }

    public int Port { get; set; }

}

public class ServiceCredentials

{

    public string Username { get; set; }

    public string Password { get; set; }

}

public class EmailServiceOptions

{

    public ServiceHost Host { get; set; }

    public ServiceCredentials Credentials { get; set; }

    public string SenderEmail { get; set; }

    public string SenderName { get; set; }

}

public class FTPServiceOptions

{

    public ServiceHost Host { get; set; }

    public ServiceCredentials Credentials { get; set; }

    public string Directory { get; set; }

}

Configuration Data Validation

We can enhance our settings validation using data annotations:

public class EmailServiceOptions

{

    [Required]

    public ServiceHost Host { get; set; }

    [Required]

    public ServiceCredentials Credentials { get; set; }

    [Required]

    [EmailAddress]

    public string SenderEmail { get; set; }

}

Tell the dependency injection to validate these settings by addition the ValidateDataAnnotations method call:

services.AddOptions<EmailServiceOptions>()

         .Bind(Configuration.GetSection(“EmailSettings”))

             .ValidateDataAnnotations();

Binding Collections

We also support setting collections. For example, providing a list of permitted operations to the FTP service:

public class FTPServiceOptions

{

    public ServiceHost Host { get; set; }

    public ServiceCredentials Credentials { get; set; }

    public string Directory { get; set; }

    public string[] PermittedOperations { get; set; }

}

These settings can be configured in appSettings.json:

    “FTPSettings”:

    {

         “Host”: “ftp.example.com”,

         “Port”: 21,

         “Username”: “ftpuser”,

         “Password”: “ftppassword”,

         “Directory”: “/uploads”,

         “PermittedOperations”: [ “read”, “write”, “delete” ]

  }

Final Thoughts

The .Net Options Pattern is a powerful method for managing application settings, providing several benefits:

  • Abstraction: Abstracts app settings from specific providers, using the general IConfiguration built by the dependency injection pattern.
  • Flexibility: Allows for easy nesting of classes and support for inheritance.
  • Validation: Supports data validation attributes to ensure the integrity of settings.

By adopting the Options Pattern, we can improve the maintainability, testability, and robustness of our applications. Stay tuned for more insights and best practices from the Britehouse Mobility team!

Further Reading

See the full code sample with additional Options Pattern features on our GitHub account here:

Click Here

Leave a Reply