Provide default configuration to your .NET applications
Tags: dotnet, csharp
Published: 2022-09-08 21:59:14

---
title: Provide default configuration to your .NET applications
published: true
date: 2022-05-23 00:00:00 UTC
tags: dotnet, csharp
canonical_url: https://www.twilio.com/blog/provide-default-configuration-to-dotnet-applications
cover_image: https://swimburger.net/media/cbkp4r3x/provide-default-configuration-to-your-net-applications.png?width=1000&height=420
---

**This blog post was written for [Twilio](https://www.twilio.com/) and [originally published at the Twilio blog](https://www.twilio.com/blog/provide-default-configuration-to-dotnet-applications).**

In a previous post I shared [how you can better configure your .NET applications for Twilio](https://www.twilio.com/blog/better-configuration-csharp-dotnet-for-twilio) and [SendGrid](https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid) using the Microsoft.Extensions.Configuration APIs. In this tutorial, I'll build upon the techniques from those tutorials and you'll learn how to store default options in one section of your configuration and override them with specific options from another section. Let me clarify using an example. Applications often send different types of emails, like welcome emails, password reset emails, offer emails, etc. Depending on the type of email, you may want to send them from a different email address. For most emails, you may be using something like 'no-reply@yourdomain.tld', but for offer emails, the replies to the email could be of value. So instead of using the same no-reply address, you could use an email address that will be routed to a sales person and maybe automatically integrates with a Customer Relationship Management (CRM) system.

In this example, you'd want to store 'no-reply@yourdomain.tld' as the default reply address, but for offer-emails, you want to use another email address, for example, 'offers@yourdomain.tld'.

## Prerequisites

To follow along, I recommend having

- Some experience with C# and .NET
- An OS that supports .NET (Windows/macOS/Linux)
- [Git CLI](https://git-scm.com/downloads)
- [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) (older or newer should work too)
- A code editor or IDE (I recommend [VS Code](https://code.visualstudio.com/Download) with [the C# plugin](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp), [Visual Studio](https://visualstudio.microsoft.com/), or [JetBrains Rider](https://www.jetbrains.com/rider/))

You can find [the source code for this tutorial on GitHub](https://github.com/Swimburger/DotNetDefaultConfiguration). Use it as a reference if you run into any issues, or [submit an issue](https://github.com/Swimburger/DotNetDefaultConfiguration/issues) if you need assistance.

## Getting started

You can apply these concepts to any scenario, but let's continue using the email scenario throughout this tutorial. 

Run the following commands to clone the tutorial project to your machine using git and navigate into the project:

```bash
git clone https://github.com/Swimburger/DotNetDefaultConfiguration.git --branch start
cd DotNetDefaultConfiguration
```

The git repository you cloned contains a .NET 6 console application that already contains some JSON configuration and code to load the configuration.

Here's what the _appsettings.json_ file looks like:

```json
{
  "Emails": {
    "Welcome": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Welcome to our service!",
      "Body": "Thank you for signing up!"
    },
    "PasswordReset": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Reset your password",
      "Body": "Click the link below to reset your password."
    },
    "Offer": {
      "FromEmail": "offers@yourdomain.tld",
      "FromName": "Jane",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe",
      "Subject": "Don't miss this offer!",
      "Body": "We have an amazing offer for you!"
    }
  }
}
```

Here's what the _Program.cs_ file looks like:

```csharp
using Microsoft.Extensions.Configuration;

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
    
EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    var emailSection = emailsSection.GetSection(emailSectionName);

    emailOptions.FromEmail = emailSection["FromEmail"];
    emailOptions.FromName = emailSection["FromName"];
    emailOptions.ToEmail = emailSection["ToEmail"];
    emailOptions.ToName = emailSection["ToName"];
    emailOptions.Subject = emailSection["Subject"];
    emailOptions.Body = emailSection["Body"];

    return emailOptions;
}

void LogEmailOptions(EmailOptions emailOptions)
{
    Console.WriteLine(@"Logging Email Option:
    FromEmail: {0}
    FromName: {1}
    ToEmail: {2}
    ToName: {3}
    Subject: {4}
    Body: {5}", 
        emailOptions.FromEmail, 
        emailOptions.FromName, 
        emailOptions.ToEmail, 
        emailOptions.ToName, 
        emailOptions.Subject, 
        emailOptions.Body
    );
}

var welcomeEmail = GetEmailOptions("Welcome");
LogEmailOptions(welcomeEmail);

var passwordResetEmail = GetEmailOptions("PasswordReset");
LogEmailOptions(passwordResetEmail);

var offerEmail = GetEmailOptions("Offer");
LogEmailOptions(offerEmail);

public class EmailOptions
{
    public string FromEmail { get; set; }
    public string FromName { get; set; }
    public string ToEmail { get; set; }
    public string ToName { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
```

The code builds configuration using only JSON as a source and then the GetEmailOptions local function is used to fetch the configuration and create an instance of  EmailOptions for the welcome-email, password-reset-email, and offer-email. You would probably dynamically generate the body of the emails and dynamically fetch the recipient email address in a real application, but this will work fine for the purpose of this tutorial.

After each EmailOptions object is created for each email section, the configuration is logged via the LogEmailOptions local function, so you can see the result of this code.

You may notice that there's a lot of redundancy in the JSON, which is why you'll introduce a defaults section in the next part of this tutorial.

Run the project using the .NET CLI:

```bash
dotnet run
# Output:
# Logging Email Option:
# FromEmail: no-reply@yourdomain.tld
# FromName: Your App Name
# ToEmail: jon@doe.com
# ToName: Jon Doe
# Subject: Welcome to our service!
# Body: Thank you for signing up!
# Logging Email Option:
# FromEmail: no-reply@yourdomain.tld
# FromName: Your App Name
# ToEmail: jon@doe.com
# ToName: Jon Doe
# Subject: Reset your password
# Body: Click the link below to reset your password.
# Logging Email Option:
# FromEmail: offers@yourdomain.tld
# FromName: Jane
# ToEmail: jon@doe.com
# ToName: Jon Doe
# Subject: Don't miss this offer!
# Body: We have an amazing offer for you!

```

You can see that the configuration is pulled from the JSON file and then written to the console.

## Add a defaults section for redundant configuration elements

You can add a Defaults section to remove the redundant configuration elements. Open _appsettings.json_ and in front of the Welcome section, add a Defaults section:

```json
{
  "Emails": {
    "Defaults": {
      "FromEmail": "no-reply@yourdomain.tld",
      "FromName": "Your App Name",
      "ToEmail": "jon@doe.com",
      "ToName": "Jon Doe"
    },
    "Welcome": {
      "Subject": "Welcome to our service!",
      "Body": "Thank you for signing up!"
    },
    "PasswordReset": {
      "Subject": "Reset your password",
      "Body": "Click the link below to reset your password."
    },
    "Offer": {
      "FromEmail": "offers@yourdomain.tld",
      "FromName": "Jane",
      "Subject": "Don't miss this offer!",
      "Body": "We have an amazing offer for you!"
    }
  }
}
```

As shown above, also remove the redundant FromEmail, FromName, ToEmail, and ToName properties from the Welcome and PasswordReset section. However, only remove the redundant ToEmail and ToName properties from the  Offer section and leave the FormEmail and FromName properties be.

Now, update the code in the _Program.cs_ file so that the configuration is retrieved from the specific email section, but if null (??), then retrieve it from the Defaults section.

```csharp
EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    var defaultsSections = emailsSection.GetSection("Defaults");
    var emailSection = emailsSection.GetSection(emailSectionName);

    emailOptions.FromEmail = emailSection["FromEmail"] ?? defaultsSections["FromEmail"];
    emailOptions.FromName = emailSection["FromName"] ?? defaultsSections["FromName"];
    emailOptions.ToEmail = emailSection["ToEmail"] ?? defaultsSections["ToEmail"];
    emailOptions.ToName = emailSection["ToName"] ?? defaultsSections["ToName"];
    emailOptions.Subject = emailSection["Subject"] ?? defaultsSections["Subject"];
    emailOptions.Body = emailSection["Body"] ?? defaultsSections["Body"];

    return emailOptions;
}
```

?? is the [null-coalescing operator](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator) which returns the value on the right side if the value on the left side is null. This allows you to succinctly check for null and if null, provide a fallback value.

Run the project again using the .NET CLI:

```bash
dotnet run
```

As you can see, the output has not changed, but now you can set configuration on specific sections and fallback on configuration from the Defaults section. 

This code gets the job done, but the same result could be achieved more eloquently by binding configuration sections to strongly-typed objects.

## Bind defaults and override defaults using the configuration binder

Instead of retrieving each configuration element individually, you can use the configuration binder to bind configuration sections to strongly-typed objects. There are two methods you can use to bind configuration sections to an instance of EmailOptions: Get<T>() and  Bind(object).

To use the Get method, pass in the desired type as a generic type parameter and the Get method create an instance of that type and bind the configuration to the object.

```csharp
var emailsSection = config.GetSection("Emails");
var emailOptions = emailsSection.GetSection(emailSectionName)
    .Get<EmailOptions>();
```

To use the Bind method, create an instance of EmailOptions and then pass it as a parameter to the Bind method.

```csharp
var emailOptions = new EmailOptions();

var emailsSection = config.GetSection("Emails");
emailsSection.GetSection(emailSectionName)
    .Bind(emailOptions);
```

What's even more interesting is that you can use the Bind method multiple times! So, if you'd like to bind both the defaults and the email specific configuration, you could do that as follows:

```csharp
var emailOptions = new EmailOptions();

var emailsSection = config.GetSection("Emails");
emailsSection.GetSection("Defaults").Bind(emailOptions);
emailsSection.GetSection(emailSectionName).Bind(emailOptions);
```

Using this technique, update the GetEmailOptions local function to create an instance of  EmailOptions, then bind the Defaults section to it, and then override the defaults by binding the specific section to it:

```csharp
EmailOptions GetEmailOptions(string emailSectionName)
{
    var emailOptions = new EmailOptions();
    
    var emailsSection = config.GetSection("Emails");
    emailsSection.GetSection("Defaults").Bind(emailOptions);
    emailsSection.GetSection(emailSectionName).Bind(emailOptions);

    return emailOptions;
}
```

## Hardcoding defaults

In addition to pulling defaults from the configuration, you may also opt to add default values directly into your code. For example, if you're developing a library for others to consume, it would be convenient for some of the configuration elements to have default values.

If you're pulling individual configuration elements, you could add default values like this:

```csharp
emailOptions.FromEmail = emailSection["FromEmail"]
    ?? defaultsSections["FromEmail"]
    ?? "no-reply@yourdomain.tld";
```

Maybe it doesn't make sense to hardcode a default value, but the configuration element is required, so you want to throw an exception if the configuration element is null. In that case, you can also use the null-coalescing operator to throw an exception inline like this:

```csharp
emailOptions.FromEmail = emailSection["FromEmail"]
    ?? throw new Exception($"Emails:{emailSectionName}:FromEmail has to be configured");
```

However, if you're using the configuration binder, you don't need to use the null-coalescing operator, and you can instead specify the default values in the object initializer like this:

```csharp
var emailOptions = new EmailOptions
{
    FromEmail = "no-reply@yourdomain.tld",
    ...
};

var emailsSection = config.GetSection("Emails");
emailsSection.GetSection("Defaults").Bind(emailOptions);
emailsSection.GetSection(emailSectionName).Bind(emailOptions);
```

Alternatively, you could specify default values in the class definition itself like this:

```csharp
public class EmailOptions
{
    public string FromEmail { get; set; } = "no-reply@yourdomain.tld";
    public string FromName { get; set; }
    public string ToEmail { get; set; }
    public string ToName { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
```

## Next steps

You've learned how to provide default configuration values through code and through a dedicated Defaults configuration section. If you haven't already, I recommend learning about more [configuration techniques in a tutorial applied to Twilio configuration](https://www.twilio.com/blog/better-configuration-csharp-dotnet-for-twilio), or applied to [SendGrid configuration](https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid).

If you found this useful, let me know and share what you're working on. I can't wait to see what you build!