Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Easy Configuration Binding in ASP.NET Core - revisited


A long while back I wrote a detailed and still relevant post that discusses ASP.NET Core's new configuration model and binding of configuration values to .NET types. In it I discussed the configuration system and specifically in how to set up configuration injection using IOptions<T>.

I really like the new model, which is much more flexible than the old, static ConfigurationManager in full framework because it provides strong typing on configuration settings out of the box. In the past I'd been using my own Westwind.Utilities.Configuration setup from Westwind.Utilities that provided much of the same functionality (interchangeable providers) - with .NET Core there's no need for a third (or for me first) party solution as the in-the-box implementation provides most of those same features. Nice.

In the process of setting up a new application over the weekend I stumbled across an even simpler and - to me at least, cleaner - approach to configuring and injecting configuration into an application without using IOptions<T>.

Let's take a look.

Create your Configuration Object

ASP.NET Core's configuration system allows binding object properties to a series of providers. By default there's a JSON provider that looks at appsettings.json file, environment variables and the UserSecrets store. The config system can bind values from all these providers (and any others you might add) into a typed configuration object which can even include nested sub-objects.

I'm working on updating my blog to .NET Core - it's time: The blog is 12+ years old and still running Webforms. For that app the beginnings of my configuration object looks like this:

public class WeblogConfiguration
{
    public string ApplicationName { get; set; }
    public string ApplicationBasePath { get; set; } = "/";
    public int PostPageSize { get; set; } = 10000;
    public int HomePagePostCount { get; set; } = 30;
    public string PayPalEmail { get; set; }
    public EmailConfiguration Email { get; set; } = new EmailConfiguration();
}

public class EmailConfiguration
{
    public string MailServer { get; set; }
    public string MailServerUsername { get; set; }
    public string MailServerPassword { get; set; }
    public string SenderName { get; set; }
    public string SenderEmail { get; set; }
    public string AdminSenderEmail { get; set; }
}

Note that you can easily nest the configuration objects which helps organizing complex configuration settings into easily segregated blocks. Here I separate out the email settings into a separate nested class.

I tend to use appsettings.json for most settings, and then use either user secrets for dev (so the values don't get shared to source control) or environment variables in production to feed in the sensitive values like passwords. Here's the relevant appsettings.json that has all the fields from my configuration mapped to a Weblog property key:

{
  "Logging": { ... }
  },
  "Weblog":
  {
    "ApplicationName": "Rick Strahl's WebLog (local)"
    "ApplicationBasePath": "/",
    "ConnectionString": "server=.;database=WeblogCore;integrated security=true;MultipleActiveResultSets=True",
    "PostPageSize": 7600,
    "HomePagePostCount": 25,
    "Email": {
      "MailServer": "mail.site.com",
      "MailServerUsername": "name@site.com",
      "MailServerPassword": "nicetry",
      "SenderEmail": "admin@site.com",
      "SenderName": "West Wind Weblog",
      "AdminSenderEmail": "admin Administration"
    }
  }
}

Setting up Injection in Startup.cs

To start, we need an IConfiguration instance which is the configuration root object. As of .NET Core 2.0 IConfiguration is a default service that can get injected automatically - it's one of the pre-configured services registered with the DI system as part of the .NET Core bootstrapping process.

The default .NET Core template now also provides IConfiguration in the Startup constructor:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}
public IConfiguration Configuration { get; }

So far, the same.

Here's the part that is not really written much about, which is that you can easily bind a configuration instance (or interface) explicitly without having to go through the IOptions<T> interface.

Instead you can simply do the following in ConfigureServices():

services.AddConfiguration()  // enable Configuration Services

var config = new WeblogConfiguration();
Configuration.Bind("Weblog", config);      //  <--- This
services.AddSingleton(config);

This provides you a filled config object instance that has values set from the various configuration stores. I can take the configured object and add it to the DI provider as a singleton. From then on I can inject the raw Configuration instance into other components or views.

To inject this configuration singleton added above is now simply a matter of requesting the WeblogConfiguration in the constructor of any class that needs it:

public class PostsController : Controller
{
    PostRepository PostRepo { get; }
    WeblogConfiguration Config  { get; }
    
    public PostsController(PostRepository postRepo, 
                           WeblogConfiguration config)
    {
        PostRepo = postRepo;
        Config = config;
    }
    ...
}

Likewise the repository constructor also receives an instance of the configuration object:

public PostRepository(WeblogContext context, 
                      WeblogConfiguration config) : base(context)

I much prefer this over injecting IOptions<T> because it's more direct and specifies the actual dependency that's needed by the components, plus it's easier to set up tests that now don't have to get an instance of IOptions<T> from somewhere.

Posted in ASP.NET Core   .NET Core  

The Voices of Reason


 

Mike Perholtz
December 28, 2017

# re: Easy Configuration Binding in ASP.NET Core - revisited

Rick, in your previous article on ASP.net Core configuration, as well as your Album Viewer code, the ConfigurationBuilder() was used to provide the location of appsettings.json or specifying a dev or prod configuration. I don't see any mention of the ConfigurationBuilder() class in this post and I am wondering how/if it still applies.


Rick Strahl
December 28, 2017

# re: Easy Configuration Binding in ASP.NET Core - revisited

@Mike - ConfigurationBuilder is now implicitly used by ASP.NET to initialize the configuration system, so you don't have to call it explicitly, unless you have a specific need to get at settings before they are initialized. The code in AlbumViewer probably does this because it predates .NET Core 2.0 where the config system was not yet initialized in Configure Services (and any middleware bits you accessed).

In short it's a bit of an edge case now...


Roger Champagne
January 09, 2018

# re: Easy Configuration Binding in ASP.NET Core - revisited

Great article, hard to be simpler! I need to manually bind environment variables to objects, and your post provided me with a great base. Many thanks for sharing!


DEnnis
January 09, 2018

# re: Easy Configuration Binding in ASP.NET Core - revisited

Very nice. Much cleaner than the IOptions approach. Thanks for posting


Gregory Gum
February 11, 2018

# re: Easy Configuration Binding in ASP.NET Core - revisited

Hi Rick,

This line is not necessary for me when I implemented this: (It throws a compile error)

services.AddConfiguration()  // enable Configuration Services

Also, you should add a link in the old post to the new one. (After a search, I landed on the old one and did not find this new one until later on a different search).

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2018