How to better configure C# and .NET applications for SendGrid
Tags: dotnet, csharp, sendgrid
Published: 2022-06-27 19:33:47

---
title: How to better configure C# and .NET applications for SendGrid
published: true
date: 2022-05-12 00:00:00 UTC
tags: dotnet, csharp, sendgrid
canonical_url: https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid
cover_image: https://swimburger.net/media/p1ohytlr/how-to-better-configure-c-and-net-applications-for-sendgrid.png?anchor=center&mode=crop&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/better-configure-csharp-and-dotnet-apps-for-sendgrid).**

There are a hundred different ways to provide configuration to your applications. For almost any programming language, you can use environment variables and _.env_ files, but configuration can also be stored in other file formats like JSON, YAML, TOML, XML, INI, and the list keeps on going. However, in some scenarios configuration isn't pulled from files, but instead is pulled from Azure Key Vault, HashiCorp Vault, or a similar vault service. It is rare that your configuration will come from a single source. Luckily, there are APIs in .NET that can help you grab configuration from multiple sources and merge them together. 

In this tutorial, you'll start with an application that sends emails using [Twilio SendGrid](https://sendgrid.com/), where the configuration is fetched directly from the environment variables. You'll then refactor the app to

- fetch configuration from multiple sources, specifically, JSON files, user-secrets, environment variables, and command-line arguments
- bind the configuration to strongly-typed objects
- inject configuration using dependency injection following the options pattern

## Prerequisites

You will need these things to follow along with this tutorial:

- 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)
- 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/))
- A free or paid Twilio account (If you [register here](https://www.twilio.com/referral/OhKZOM), you'll receive $10 in Twilio credit when you upgrade to a paid account!)
- A Twilio SendGrid account. Sign up [here](https://sendgrid.com/free/?source=twilio-blog) to send up to 100 emails per day completely free of charge.

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

## Configure your SendGrid account to send emails

There are two things you need to configure before you can send emails. First, you’ll need to set up **Sender Authentication**. This will verify that you own the email address or domain that you will send emails from. Second, you’ll need to create a **SendGrid API Key** with permission to send emails.

### Sender Authentication

To quickly get started, you will use **Single Sender Verification** for this tutorial. This verifies that you own the email address that the application will send emails from. Single Sender Verification is great **for testing purposes** , but it is not recommended for production.

Twilio recommends [Domain Authentication](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication/) for production environments. An authenticated domain proves to email providers that you own the domain, and removes the "via sendgrid.net" text that inbox providers would otherwise append to your from address.

To set up Single Sender Verification, click the **Settings** tab in the left menu. Once the settings tab opens, click **Sender Authentication**.

![Side-navigation with a Settings group and multiple links in the group. The link "Sender Authentication" is highlighted.](https://twilio-cms-prod.s3.amazonaws.com/images/nPSCS5q1BytqQKT4dcVRz-BE4seRapA7j5xNz4X4rxYWc-.width-500.png)

Then, click **Get Started** under the **Single Sender Verification** section.

![A button saying "Get Started" to verify an individual email address.](https://twilio-cms-prod.s3.amazonaws.com/images/36Ob7tHuD-2qb-P2ECdPKHY9qEPLl_XfG4NoXfAs7mMt0m.width-500.png)

This opens a form in the right-side panel. Fill out the form with your information and email address.

![A form to create a sender. The form asks for contact information including the email address that will be used to send emails from.](https://twilio-cms-prod.s3.amazonaws.com/images/IUSVGXUjozYbyqKUdaNTd3tkxjjO7zQct6155C_k54ppRQ.width-500.png)

Click **Create** after filling out the form. Another panel will appear on the right, asking you to confirm your email address in your email inbox.Go to your personal email inbox, open the email from SendGrid, and click Verify Single Sender.

![An email from SendGrid with a button saying "Verify Single Sender".](https://twilio-cms-prod.s3.amazonaws.com/images/iPFFYg6ptFsUG3P3iw7TAaOLTyx1UkoaMKRy75wJzFp3fv.width-500.png)

Your email address has been verified. You can now use it to send emails!

### Create a SendGrid API Key to send emails

Back on the SendGrid website, click **API Keys** under the **Settings tab** , then click **Create API Key** in the top right-hand corner. This opens another form in the right-side panel. 

Give your API Key a meaningful name. You can assign different permissions to the API Key. For optimal security, you should only give the minimum amount of permissions that you need.

Next, click Restricted Access.

![A form to create an API Key. The form asks for a name for the key and what permissions to grant to the key.](https://twilio-cms-prod.s3.amazonaws.com/images/iFCCrNrpOwKqVAacydgTMnsmfKjxdwtGuHmb4v_JlGbPr1.width-500.png)

Scroll down to the **Mail Send** accordion item and click on it to reveal the permissions underneath. Drag the slider to the right for the **Mail Send** permission.

![A list of permissions that you can control using a slider bar next to each permission. The "Mail Send" accordion item is expanded to reveal the "Mail Send" permission underneath.](https://twilio-cms-prod.s3.amazonaws.com/images/M1jOW-BEJAn8NGWA6B-IyGnrs18M6hRliRTbKOhn987TYv.width-500.png)

Scroll to the bottom of the form and click **Create & View**. The API key will now be displayed on your screen. 

You will not be able to retrieve the API key again once you leave this screen, so make sure you copy the API Key somewhere safe.

![API key is displayed to copy and store elsewhere. Key will not be shown again.](https://twilio-cms-prod.s3.amazonaws.com/images/N2zTJIaJNBJzH0HqvEPPUaVDOWrjkDqqDOYRgdH-FauuvI.width-500.png)

With the sender verified and the API Key created, you’re ready to write some code!

For any of the upcoming commands and code, replace the following placeholders like this:

- Replace `[SENDGRID_API_KEY]` with the API Key you just took note of
- Replace `[FROM_EMAIL_ADDRESS]` and `[FROM_NAME]` with the email address you verified earlier and the name you want your recipients to see.
- Replace `[TO_EMAIL_ADDRESS]` and `[TO_NAME]` with the email address and name of the desired recipient of the email.

## Get configuration directly from environment variables

To get the project up and running quickly on your machine, open your preferred shell, and use the following git command to clone the source code:

```bash
git clone https://github.com/Swimburger/SendGridOptionsPattern.git --branch Step1
```

Then navigate into the _SendGridOptionsPattern_ folder using `cd SendGridOptionsPattern`. This project is a console application with only one C# file, _Program.cs_, which has the following code:

```csharp
using SendGrid;
using SendGrid.Helpers.Mail;

var apiKey = Environment.GetEnvironmentVariable("SendGridApiKey");
var fromEmailAddress = Environment.GetEnvironmentVariable("FromEmailAddress");
var fromName = Environment.GetEnvironmentVariable("FromName");
var toEmailAddress = Environment.GetEnvironmentVariable("ToEmailAddress");
var toName = Environment.GetEnvironmentVariable("ToName");

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = new EmailAddress(fromEmailAddress, fromName),
    Subject = "Sending with Twilio SendGrid is Fun",
    PlainTextContent = "and easy to do anywhere, especially with C#"
};
message.AddTo(new EmailAddress(toEmailAddress, toName));
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
```

The application pulls the SendGrid API Key, as well as the email address and name for the sender and recipient from the environment variables. The code then creates a new `SendGridClient` with the API key, creates an email message using `SendGridMessage`, and then sends the email.

The application uses the [SendGrid library for C# and .NET](https://github.com/sendgrid/sendgrid-csharp) to authenticate with and send emails through the SendGrid API. The [SendGrid NuGet package](https://www.nuget.org/packages/SendGrid) has been added into the project as part of the source code you cloned. You can find this dependency in the csproj-file.

Before you can run this application, you'll need to configure the environment variables that the project depends on. Back in your shell, set these environment variables using the following commands. 

If you're using Bash or a similar shell:

```bash
export SendGridApiKey=[SENDGRID_API_KEY]
export FromEmailAddress=[FROM_EMAIL_ADDRESS]
export FromName="[FROM_NAME]"
export ToEmailAddress=[TO_EMAIL_ADDRESS]
export ToName="[TO_NAME]"
```

If you're using PowerShell:

```powershell
$Env:SendGridApiKey = "[SENDGRID_API_KEY]"
$Env:FromEmailAddress = "[FROM_EMAIL_ADDRESS]"
$Env:FromName = "[FROM_NAME]"
$Env:ToEmailAddress = "TO_EMAIL_ADDRESS]"
$Env:ToName = "[TO_NAME]"
```

If you're using CMD:

```bash
set "SendGridApiKey=[SENDGRID_API_KEY]"
set "FromEmailAddress=[FROM_EMAIL_ADDRESS]"
set "FromName=[FROM_NAME]"
set "ToEmailAddress=[TO_EMAIL_ADDRESS]"
set "ToName=[TO_NAME]"
```

Once the environment variables have been set, you can run the project using the following .NET CLI command:

```bash
dotnet run
```

The application will send an email from your verified email address to the recipient.

![An email with title "Sending with Twilio SendGrid is Fun" and body "and easy to do anywhere, especially with C#](https://twilio-cms-prod.s3.amazonaws.com/images/WG_UzQM1y5QewEJDicPNRdrirtn9MBkI-Px_GkdYszJt8_.width-500.png)

When the SendGrid API responds with a successful HTTP status code, that does not necessarily mean that the email was successfully received by the recipient. It does however mean that the SendGrid API has accepted your request and will send the email. If the recipient does not receive the email, you can use the [Email Activity Feed](https://app.sendgrid.com/email_activity) to find out why.

Pulling the configuration from the environment variables works great, but instead of using environment variables, you could've simply hardcoded all the configuration, right?  
Many of these configuration elements, especially the API Key, are sensitive secrets which you do not want to share with others. By pulling them from external configuration like the environment variables, you ensure that you don't accidentally check them into your public source code for everyone to see. And whenever someone else wants to run the same code, they can configure their own API Key configuration into the environment variables and run it. 

This is why in SendGrid samples, we will always use environment variables. Although it's an extra step compared to hard coding configuration, it is the fastest way to get you up and running with SendGrid without compromising your API Key by accident.

However, in .NET, there's a better way to retrieve external configuration, using the `ConfigurationBuilder`.

## Build your Configuration with the .NET configuration builder

There are many ways to configure .NET applications, and typically the configuration is composed from multiple sources. ASP.NET Core originally introduced APIs to easily load configuration from a bunch of different sources using [configuration providers](https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration#configuration-providers) and merge them all together with the `ConfigurationBuilder`. These APIs were moved into the [Microsoft.Extensions.Hosting NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) so it could be used in any .NET application.

Not only can you use the new Configuration APIs on .NET (Core) — you can even [use them on .NET Framework](https://swimburger.net/blog/dotnet/using-configurationproviders-from-microsoft-extensions-configuration-on-dotnet-framework). .NET Framework has its own configuration APIs, but they are less intuitive and dated compared to the new Configuration APIs.

Back in your shell, run the following .NET CLI commands to add NuGet packages for the configuration extensions and some of the configuration providers:

```bash
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.CommandLine
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
```

- The [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration) package contains the main APIs for building configuration and also the JSON configuration provider.
- The [Microsoft.Extensions.Configuration.CommandLine](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.CommandLine) package adds a configuration provider that builds configuration from command-line arguments.
- The [Microsoft.Extensions.Configuration.EnvironmentVariables](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.EnvironmentVariables) package adds a configuration provider that builds configuration from environment variables.
- The [Microsoft.Extensions.Configuration.UserSecrets](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.UserSecrets) package adds a configuration provider that builds configuration from the [Secret Manager](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets#enable-secret-storage).

To start using the configuration APIs, update the _Program.cs_ file with the following code:

```csharp
using System.Reflection;
using Microsoft.Extensions.Configuration;
using SendGrid;
using SendGrid.Helpers.Mail;

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
    .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true, reloadOnChange: false)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();

var apiKey = config["SendGrid:ApiKey"];
var fromEmailAddress = config["Email:From:Email"];
var fromName = config["Email:From:Name"];
var toEmailAddress = config["Email:To:Email"];
var toName = config["Email:To:Name"];
var emailSubject = config["Email:Subject"];
var emailBody = config["Email:Body"];

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = new EmailAddress(fromEmailAddress, fromName),
    Subject = emailSubject,
    PlainTextContent = emailBody
};
message.AddTo(new EmailAddress(toEmailAddress, toName));
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
```

The resulting app is the same as before but is now loading configuration from multiple sources instead of directly from the environment variables. The `ConfigurationBuilder` is a class that uses the [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) so you can chain together multiple configuration providers using the `Add…` extension methods. When configuration elements coming from different providers have the same key, the configuration added later to the chain will overwrite the configuration added earlier in the chain. This means that the order in which you add configuration providers matters! In this case:

- User Secrets overwrites configuration from **JSON**
- **Environment Variables** overwrites configuration from **User Secrets and JSON**
- **Command-Line Arguments** overwrites configuration from **User Secrets, JSON, and Environment Variables**.

The `optional` parameter specifies whether or not the application should throw an exception if the underlying source is missing. The `reloadOnChange` parameter specifies whether or not to update the configuration when the underlying source changes. For example, if there's an issue in your production application, but for performance reasons, the logging level is set to `Warning` in _appsettings.json_, you can change the logging level to `Debug`. Once you save the file, the application begins writing logs from the `Debug` level and up, without having to restart your application!

After the configuration is built, the app will grab the configuration elements from the configuration, but notice how the configuration keys now use two different prefixes: `SendGrid:` and `Email:`.

You can organize your configuration into hierarchical sections by adding colons `:` as separators. So in this case, `ApiKey` is part of the `SendGrid` section, and all the other configuration is part of the `Email` section. You can also nest sections as shown in the `Email` section. The `Email` section contains child sections `From` and `To` which hold on to the email address and name of the sender and recipient. There are a lot of different ways you could structure your configuration, but how you organize your configuration all depends on your needs.

Now that the application loads configuration from multiple sources, in which source should you store configuration? It depends on the configuration, your use-case, and personal preference:

- Use JSON for configuration that does not contain secrets or other sensitive information.
- Use **User Secrets** only for local development to configure secrets and other sensitive information.
- Use **Environment Variables** for environment specific configuration including secrets or other sensitive information. However, User Secrets is preferred for local development. Environment Variables are a powerful source of configuration because all operating systems, container technology, and cloud infrastructure supports them.
- Use **Command-Line Arguments** to configure execution specific settings.

Create a new file called _appsettings.json_ and add the following JSON:

```json
{
  "Email": {
    "From": {
      "Email": "[FROM_EMAIL_ADDRESS]",
      "Name": "[FROM_NAME]"
    },
    "Subject": "Ahoy from JSON!",
    "Body": "Thank you for signing up to our website. See you around!"
  }
}
```

The _appsettings.json_ file will provide the `FromEmailAddress`, `FromName`, `Subject`, and `Body` configuration, even though it would also make sense to pass this configuration in as command-line arguments.

By default, in a console app, the _appsettings.json_ file will not be copied to the output of the project. As a result the configuration provider will not find the underlying source, which means the configuration in your JSON will not be loaded. To make sure the the _appsettings.json_ file is copied to the output, add a `Content` node to the csproj-file as shown below:

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>...</PropertyGroup>
  <ItemGroup>...</ItemGroup>

  <ItemGroup>
    <Content Include="appsettings.json" CopyToOutputDirectory="Always" />
  </ItemGroup>
</Project>
```

To start using the Secret Manager, you first need to initialize it for your project using the following command:

```bash
dotnet user-secrets init
```

Now you can set the `SendGrid:ApiKey` configuration like this:

```bash
dotnet user-secrets set SendGrid:ApiKey [SENDGRID_API_KEY]
```

It's likely you would configure the API Key as an environment variable in other environments, but since you're running this locally, the Secret Manager is preferred. So, in this case, you don't need to configure any environment variables.

The application now has configuration for who the email will be sent from, but not who the email will be sent to. Instead of configuring it using one of the previous sources, run the project using the .NET CLI and pass it in as a command-line argument:

```bash
dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]"
```

Just like before, the recipient will receive an email, but now the subject will be "Ahoy from JSON!".

Run the command again, but overwrite the `Email:Subject` configuration using a command-line argument:

```bash
dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"
```

Now the recipient will receive an email with the subject "Ahoy from the CLI".

The end result may be the same, but your application is a lot more flexible now because it can be configured in many different ways. However, these are not the only configuration providers. [Microsoft has 5 more configuration providers](https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration#configuration-providers), and if those don't meet your needs, you can also develop your own provider or use someone else's. 

Some operating systems and shells may not allow you to use colons. In that case you can use a double underscore `__` as a separator, and .NET will replace it with the colon for you.

## Bind configuration to strongly-typed objects

You just learned how to use the configuration builder to get configuration from multiple sources, but the amount of configuration can quickly get out of hand. A more manageable solution to this is to bind your configuration to strongly-typed objects.

To starting binding configuration, add the [Microsoft.Extensions.Configuration.Binder NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Binder) with the following command:

```bash
dotnet add package Microsoft.Extensions.Configuration.Binder
```

Update the _Program.cs_ file with the code below:

```csharp
using System.Reflection;
using Microsoft.Extensions.Configuration;
using SendGrid;
using SendGrid.Helpers.Mail;

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
    .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true, reloadOnChange: false)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();

var apiKey = config["SendGrid:ApiKey"];

var emailOptions = config.GetSection("Email").Get<EmailOptions>();

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = emailOptions.From,
    Subject = emailOptions.Subject,
    PlainTextContent = emailOptions.Body
};
message.AddTo(emailOptions.To);
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
```

At the bottom of _Program.cs_, there is a new class which has properties with names that match your configuration. By matching the name of the properties and the keys of the configuration, you will be able to bind the configuration to instances of said class. You can grab sections from the configuration using `config.GetSection("YourSectionName")` and then bind the section to strongly-typed objects using `.Get<YourClass>()`. This way, the `Email` section is bound to instances of `EmailOptions` which is then used to send the email as before. 

For the `EmailOptions` class, the `From` and `To` properties are defined using the `EmailAddress` class provided by the SendGrid library. The `EmailAddress` class contains two properties: `Email` and `Name`. Because these properties names match the names of your configuration, even these nested objects will be bound.

If you run the project now, you will continue to get the same result:

```
dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"

```

If `reloadOnChange` is enabled on the configuration providers, and the configuration changes in the configuration source, the configuration object will be updated, but any strongly-typed object that has already been bound will not be updated accordingly.

## Swap configuration builder with the default host builder

If you've used the ASP.NET templates, you may notice that the configuration has already been set up without any `ConfigurationBuilder` code. That's because ASP.NET templates use the default [Web Host](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/web-host?view=aspnetcore-6.0) which sets up the default configuration, logging, dependency injection, web related functionality, and more. 

If you want the same configuration, logging, and dependency injection from the web host builder, but not the web related functionality, you can use the [Generic Host](https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host) instead of the Web Host.

Add the [Microsoft.Extensions.Hosting NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) using the .NET CLI:

```bash
dotnet add package Microsoft.Extensions.Hosting
```

In _Program.cs_, Replace the `using` statements and the `ConfigurationBuilder` code with the following code:

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SendGrid;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args).Build();
var config = host.Services.GetRequiredService<IConfiguration>();
```

Instead of building the configuration yourself, the project now uses the defaults that come with the Generic Host. The code then retrieves the configuration from the dependency injection container (`host.Services`), and continues using the configuration object as before.

The default Generic Host will build configuration similarly to how you did it yourself, but there are some differences. You can find an [overview of the Generic Host defaults in the Microsoft Documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0#default-builder-settings).

The default Generic Host builds configuration slightly differently. More specifically, your user secrets will only be loaded if the `Environment` is configured as `Development`. There are [multiple ways to configure the environment](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments#environments), but for this tutorial, pass in the `Environment` argument when you run your project, like this:

```bash
dotnet run --Environment Development  --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"
```

## Get configuration from dependency injection using the options pattern

Now that you're using the Generic Host, you can start using the dependency injection (DI) that comes with it, just like you can on ASP.NET applications.

Replace your existing code in _Program.cs_ with the following code:

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.Configure<EmailOptions>(context.Configuration.GetSection("Email"));
        services.AddTransient<EmailSender>();
    })
    .Build();

var config = host.Services.GetRequiredService<IConfiguration>();

var apiKey = config["SendGrid:ApiKey"];
var client = new SendGridClient(apiKey);

var emailSender = host.Services.GetRequiredService<EmailSender>();
await emailSender.SendEmail(client);

public class EmailSender
{
    private readonly EmailOptions emailOptions;

    public EmailSender(IOptions<EmailOptions> emailOptions)
    {
        this.emailOptions = emailOptions.Value;
    }

    public async Task SendEmail(ISendGridClient client)
    {
        var message = new SendGridMessage
        {
            From = emailOptions.From,
            Subject = emailOptions.Subject,
            PlainTextContent = emailOptions.Body
        };
        message.AddTo(emailOptions.To);
        var response = await client.SendEmailAsync(message);

        // A success status code means SendGrid received the email request and will process it.
        // Errors can still occur when SendGrid tries to send the email. 
        // If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
        Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
    }
}

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
```

You can use the `ConfigureServices` method on the host builder and pass in a lambda to add more services to the DI container. In this lambda, the `Email` configuration section is added as configuration using `services.Configure<YourOptions>`. As a result, your configuration will be injected wherever DI is supported by adding a parameter of type `IOptions<YourOptions>` to your constructor or method signature. Microsoft calls this the [Options pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#bind-hierarchical-configuration-data-using-the-options-pattern).

In addition to `IOptions<T>`, there's also `IOptionsSnapshot<T>`, `IOptionsMonitor<T>`, and `IOptionsFactory<TOptions>` which each have different behavior and use-cases. Learn more about the [differences between these options interfaces at Microsoft's documentation](https://docs.microsoft.com/en-us/dotnet/core/extensions/options#options-interfaces).

After configuring the options, the program will add the `EmailSender` class to the CI container. `EmailSender` is a new class that will be responsible for sending Emails. Now that the DI container has been built, you can retrieve the services from it. So now you have your `EmailOptions` injected anywhere that supports DI. 

Now, the code will retrieve an instance of `EmailSender` from the DI container, which is used to send an email using the `​​SendEmail` method.

When `EmailSender` is created by the DI container, the container injects an instance of `IOptions<EmailOptions>` into the constructor of `EmailSender`. `EmailSender` grabs the message options using the `.Value` property and uses it in the `SendEmail` method to send an email.

## Add SendGridClient to the Dependency Injection Container

You've made your application more flexible and configurable, but you can also further integrate SendGrid into your application. The SendGrid SDK for .NET has an extra [NuGet package that will add the SendGrid client to the dependency injection container](https://www.nuget.org/packages/SendGrid.Extensions.DependencyInjection/).

Run the following command to add the SendGrid DI package:

```bash
dotnet add package SendGrid.Extensions.DependencyInjection
```

Then update the _Program.cs_ file like this:

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Extensions.DependencyInjection;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddSendGrid(options =>
            options.ApiKey = context.Configuration["SendGrid:ApiKey"]
        );
        services.Configure<EmailOptions>(context.Configuration.GetSection("Email"));
        services.AddTransient<EmailSender>();
    })
    .Build();

var emailSender = host.Services.GetRequiredService<EmailSender>();
await emailSender.SendEmail();

public class EmailSender
{
    private readonly ISendGridClient sendGridClient;
    private readonly EmailOptions emailOptions;

    public EmailSender(IOptions<EmailOptions> emailOptions, ISendGridClient sendGridClient)
    {
        this.sendGridClient = sendGridClient;
        this.emailOptions = emailOptions.Value;
    }

    public async Task SendEmail()
    {
        var message = new SendGridMessage
        {
            From = emailOptions.From,
            Subject = emailOptions.Subject,
            PlainTextContent = emailOptions.Body
        };
        message.AddTo(emailOptions.To);
        var response = await sendGridClient.SendEmailAsync(message);

        // A success status code means SendGrid received the email request and will process it.
        // Errors can still occur when SendGrid tries to send the email. 
        // If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
        Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
    }
}

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
```

The application adds the SendGrid client to the DI container using the `services.AddSendGrid` method.   
To configure the options of SendGrid, another lambda is used and passed into `AddSendGrid`. The lambda-inception can be tricky, so make sure to verify all your parentheses match the code above.

Instead of constructing the `SendGridClient` as you did before, the `ISendGridClient` will now be injected into the constructor of the `EmailSender` class, and then used to send an email as before.

## Why so complicated?

You went from approximately 12 straightforward lines of code, to approximately 26 lines of code with classes, interfaces, lambda expressions, generic types, and more.  
Why complicate the code so much? 

For a small sample where you want to go from point A to point B as fast as possible, you can use environment variables and be done with it, but for real-world applications, although it requires extra setup up-front, the techniques from this tutorial will decrease the amount of code you need to write and increase the flexibility and maintainability of your solution.

## Better .NET configuration

After following this tutorial, you have a learned how to use .NET's configuration APIs to:

- securely configure secrets and sensitive information
- retrieve configuration from multiple sources
- override configuration from one source with another source
- add configuration to the dependency inject container
- inject configuration into your classes

Congratulations on making it to the end of a long post about configuration! 👏  
You can start including these techniques for SendGrid, Twilio, or really any .NET applications! If you run into any problems, you can refer to the [source code for this tutorial on GitHub](https://github.com/Swimburger/SendGridOptionsPattern), or you can submit an issue on the GitHub repository. In addition to the end result, you can find each step in a separate branch ([Step 1](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step1), [2](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step2), [3](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step3), [4](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step4), [5](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step5), and [6](https://github.com/Swimburger/SendGridOptionsPattern/tree/Step6))

Let me know what you're working on. I can't wait to see what you build!