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


On this page:

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.

#PAGEBREAK

Compare to using IOptions<T>

Just so you know what I'm talking about when I say IOptions<T> implementation: Here's an example of how to set up the same behavior using IOptions<T> instead of the configuration singleton.

services.AddOptions();

var section = Configuration.GetSection("Weblog");
services.Configure<WeblogConfiguration>(section);

You can then inject into a controller's constructor like this:

public PostsController(PostRepository postRepo, 
                       IOptions<WeblogConfiguration> options)
{
    PostRepo = postRepo;
    Config = options.Value;  // note the indirect reference :-(
}

Obviously this isn't any more complicated, but it does require an extra layer of abstraction. IOptions<T> is just that - an abstraction wrapper without any real feature benefits. That may change in the future, but for now IOptions is just a wrapper around the configuration instance via the Value property.

IOptionsSnapshot can reload changed Config Settings

One advantage to using IOptions<T> or more specifically IOptionsSnapshot is that it can detect changes to the configuration source and reload configuration as the application is running.

For application level code this is perfectly fine, but if I do this for my repository which lives in a separate business project independent of the main application:

public PostRepository(WeblogContext context, 
                      IOptions<WeblogConfiguration> config) : base(context)

having to pass in IOptions makes the configuration a little less accessible for testing as you now need to provide IOptions rather than a plain POCO object.

IOptions provides additional features

After I posted this entry both Damien Edwards and David Fowler chimed in on Twitter that IOptions<T> provides a host of additional features that allow for composition through the lifetime of IOptions<T>.

In short there are potential benefits to using IOptions<T> over a raw configuration instance as the instance is essentially managed and additional services can be applied.

I'm having a hard time visualizing the use case David is talking about that I can't directly apply against a POCO instance that is passed through an application level configuration pipeline - if you can think of a good example or use case please chime in with a comment I would love to elaborate this point here.

Personally I've been using a more direct approach and haven't run into any issues with raw configuration instances, mainly because in the applications I tend to build the configuration is not all that dynamic. In fact I can't think of configuration that needs to be any more dynamic than the core features of the configuration system (via multiple providers) already handles.

Raw Configuration Value Binding

For completeness sake note that you can also bind string values by configuration path using the Configuration object indexer, which you can inject using the IConfiguration interface which in turn references a ConfigurationRoot instance. Using this object you get raw Configuration API access using string formatting where access a particular path key is path:key with 'paths' separated by : characters. You can think of this lower level API in the same way Configuration.AppSettings[] was used in full framework to provide string based configuration access, plus the benefits of multiple providers and nested configuration paths.

To use it you can do this:

var connectionString = Configuration["Weblog:ConnectionString"]
var mailServer = Configuration["Weblog:Email:MailServer"];

which can be sufficient if you have only a few configuration values in your app. Given how easy it is to create typed object mapped configuration though I find it hard to recommend this approach except for dynamic key access or startup code where the configuration objects may not have been set yet.

Static Instance?

As many of you know I'm not the biggest fan of DI for all things. I think there are a few things - and especially Configuration - that need to be easily and universally accessible in all situations. Especially in older versions of .NET Core it was painful to get at the configuration objects say inside of business or a system level component since configuration wasn't automatically injected by default. That's been fixed, but it can still be difficult to get access to the DI service context in some cases.

If you're building a generic component that needs to be reused in many different environments that you may not control, there's no guarantee that DI is available and configured. Configuration is a critical component that often is needed deep in the bowels of other components or necessarily static logic where it's not easy to get access to DI injected components. It never made sense to me to force DI on simple few-line helpers when a static function that have no other dependencies.

Long story short: It is sometimes useful to be able to get at a static instance of configuration and while I try to avoid introducing singleton statics like this in most cases, I think configuration is one of those cases where it makes sense (at least to me).

So, I decided to also add a static property that holds the Current instance:

 public class WeblogConfiguration
 {
    public static WeblogConfiguration Current;

    public WeblogConfiguration()
    {
        Current = this;
    }
}

Since the configuration object is a singleton anyway, the Current property is implicitly set only once when the code in ConfigureServices() initially binds the config instance and it points at the same object instance that also gets injected. After that you can then use either DI whenever possible - ie. most cases - and the static property in those few special cases when it's difficult to get access to the DI context.

In my Weblog for example, I'm copying over a lot of old helper code from the old Weblog application and there are static function helpers that generate a bunch of small HTML bits like this:

public static HtmlString ShareOnFacebook(string url)
{
    var baseUrl = WeblogConfiguration.Current.ApplicationBasePath;
    string link =
$@"<a href=""https://www.facebook.com/sharer/sharer.php?u={url}&display=popup"" target=""_blank"">
      <img src=""{baseUrl}images/shareonfacebook.png"" style=""height: 20px;padding: 0;"" />
</a>";

    return new HtmlString(link);
}

The static property makes this code work easily without having to refactor all of the ported functions in this class to a (pointless) class wrapper.

This gives me the best of both worlds: I get the ability to inject configuration where I can get at the DI context (just about all new code), and use the static in the few cases when DI is not easily available (legacy code).

Many Options - Choice is good

In ASP.NET Core there are many ways to handle configuration and that's a good thing. As I mentioned I ditched my own custom solution in favor of the new configuration system in .NET Core which is good enough out of the box. The only thing I'm really missing is an easy way to update configuration stores and force initial values to be written, but overall that's a minor concern and doesn't make sense for all the supported providers (how do you update Environment variables loaded from a startup script for example?)

The good news is that the configuration engine is very flexible and provides a number of different ways to get at configuration objects.

  • Raw Configuration[path:key]
  • IOptions<T> binding to a Config Section
  • Configuration.Bind() to bind to a Config Section
  • Static Props if you want it

Choice is good - for now I'm choosing the instance based approach because to me at least it seems it's the most intuitive for working with configuration. Your mileage may vary...

Resources

this post created and published with Markdown Monster
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).


Todd Menier
March 02, 2018

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

I believe you could have the best of both worlds by registering your settings like this:

services.AddTransient(sp => sp.GetService<IOptions<AppSettings>>().Value);

By best of both worlds I mean this would allow you to inject your AppSettings object directly into services, yet still have just-in-time binding and all the other benefits of using IOptions that David Fowler mentioned.

To confirm this works as expected, I use IOptionsSnapshot above instead of IOptions and verified that changes to appsettings.json are indeed picked up without restarting the app.

@Rick I'd love to get your thoughts on this.


Rick Strahl
March 20, 2018

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

@Todd - yes that looks like a good solution although as long as we know that the config isn't being re-read on every request 😃


Steve Collins
April 02, 2018

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

Hi Rick,

This and your previous post on configuration inspired me to write 3 posts on my blog. The latest is at https://stevetalkscode.co.uk/configuration-bridging-part-3 which takes Todd's idea of using a lambda a step further by using a bridging class which allows injection of other classes into the settings class for functionality such as settings validation and decryption.

Would appreciate your thoughts.

Steve


JB
September 07, 2018

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

Thank you for the write up on binding configuration to an object. I was handling parsing IConfiguration manually until my app began to grow and become more complex. Having been aware of binding, I came looking specifically for an example of how to do so and found your page. I was hoping to see an example of binding a collection property in my config object with an array in my config file. I will try it now and see what happens. Thanks for your example to get me to the point I need to try this!


Chris
September 12, 2018

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

You can work around the constructor injection issue by implementing IOptions:

	public class S3Configuration : IOptions<S3Configuration>
	{
		public string BucketName { get; set; }
		// etc.

		[JsonIgnore]
		public S3Configuration Value => this;
	}

This lets you do a new MyController(new S3Configuration()) in your tests, although it's still messy with DI.


Chris
September 12, 2018

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

I went ahead and made this extension method to cater for both IOptions and its concrete setting (POCO)


Bilal Haidar
September 16, 2018

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

Hello @Rick,

if you can think of a good example or use case please chime in with a comment I would love to elaborate this point here.

Reading David's tweet above, I believe, when you are building a modular application in ASP.NET Core, where multiple modules need to "inject" something that needs to be retrieved later by a certain "Core" module (assuming you need to collect settings from all modules in the app). Then, by using services.Configure() multiple times (each module will be using it), then this way, the ASP.NET Core Framework would collect all instances from all modules and make them available for you.

For instance, you might define a Dictionary read-only property inside a custom Options class and then each module would read that dictionary and append to it some settings or whatever the business logic you need.

The end result is that the custom Options class having its read-only dictionary property, collecting all values that were added by all modules.

That's how I could think of it!

What do you think?


zameer fouzan
October 22, 2018

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

I used your singleton example in my application and noticed that as of version 2.1.403,

services.AddConfiguration()

is not supported and all works just fine without it.

Not sure if it is done implicitly by IConfiguration. Need to figure it out .

Thanks for a great example.


pixelda
October 29, 2018

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

I'm using MVC 2.1 and it is likely that services.AddConfiguration() is now part of the internal start up. As always Rick, an informative and useful article

 

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