Practical ASP.NET

Accessing Configuration Settings in ASP.NET Core

In ASP.NET Core, your web.config file with its <appsettings> section is gone. The replacement is a more extensive and configurable system that you can leverage to simplify configuring your objects.

In ASP.NET Core, the code in the various initialization files in the Startup folder and the XML in the app.config file are gone, collapsed into the code in the Startup class in your project's Startup.cs file. The component that isn't replaced by the Startup class, however, is the appsettings section of your web.config file: That was where you put the text settings that you didn't want to hard code into your application's code. However, instead of an appsettings element in your app.config file, you now have an appsettings.json file.

While appsettings.json holds your settings, to retrieve those settings you'll use ASP.NET Core's IConfiguration object. Once you've retrieved settings, ASP.NET Core also provides the IOptions interface that allows you to share those settings among your application's objects.

Reading Your Settings
In a previous column, I used an OrdersService object as a case study. That OrdersService object accepted two configuration parameters: OrdersType and CutOffDate. To support that object, I could store that configuration information in the appsettings file like this:

{
   "OrdersService": {
      "OrderType": "Archived",
      "CutOffDate": "17-Feb-2018"
   },
   ... more settings ... 

To retrieve just one of those values, I can pass the path name to the setting to ASP.NET Core's IConfiguration object. The default code in the Startup class's constructor requests the IConfiguration object, and the default code in the constructor stuffs that object into a read-only property called Configuration. Here's what that code looks like this:

public IConfiguration Configuration { get; }

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

Using that IConfiguration object, I can retrieve my OrderType setting from appsettings with code like this:

string otype = Configuration["OrdersService:OrderType"];

However, my OrderType is intended to be a value from an enumeration called OrderTypes, not a string. In addition, my CutOffDate setting (when I get around to retrieving it) needs to be a date, not a string.

Adding the necessary conversion code to convert the string values that the Configuration object gives me into the values that my OrdersService class requires results in code like this:

string otypeString = Configuration["OrdersService:OrderType"];
OrderTypes otype = (OrderTypes) Enum.Parse(typeof(OrderTypes), otypeString);
DateTime cutoffDate = DateTime.Parse(Configuration["OrdersService:CutOffDate"]);

Though, in both of these cases, using TryParse rather than Parse (and supplying default values if TryParse fails) would probably be safer.

Creating IOptions Objects
However, if I continue along this path, it's going to be painful as the number of settings increase. As an alternative, I can define a class with property names that match the names in the appsettings file. For example, a class that corresponds to my sample OrdersService entry in the appsettings file would look like this:

public class OrderOptions
{
   public OrderTypes OrderType { get; set; }
   public DateTime CutoffDate { get; set; }
}

To use this class to retrieve the value, I use the Configuration object's GetSection method. That gives me an IConfigurationSection object that represents the section with my settings. Rather than use that IConfigurationSection directly, however, I can pass it to the IServiceCollection's Configure method. The Configure method converts that IConfigurationSection into an IOptions object and adds it to your application's IServiceCollection.

The code that does that is pretty simple:

IConfigurationSection sec = Configuration.GetSection("OrdersService");
services.Configure<OrderOptions>(sec);

Retrieving Options
Now, in your Controllers, you can retrieve that IOptions object just by asking for it in the Controller's constructor. When I need my OrderOptions object, I retrieve it from the IOptions object's Value property.

Typical code would look like this:

public class HomeController : Controller
{
  public HomeController(IOptions<OrderOptions> op)
  {
    DateTime dt = op.Value.CutoffDate;
    OrderTypes ot = op.Value.OrderType;
  }

Not only is the code that retrieves the configuration values simpler, but the conversion to DateTime and my enumerated value is automatically handled for me. Using an IOptions object also allows me to pass those options around my application and modify them as needed.

I'm doing all of this in the ConfigureServices method of my application's Startup class. I can also use the IOptions object within my ConfigureServices method. I first need to create a ServiceProvider object from the IServiceCollection that the IOptions object was added to. Once I've done that, I can use the ServiceProvider's GetService method (referencing my IOptions object) to retrieve the IOptions object. I can then pass that IOptions object to any class that needs it.

In this example, I retrieve my IOptions object, extract the OrderOptions object, and pass it to the configuration process for my OrdersService object (see the article I referred to earlier for the details of that configuration process):

ServiceProvider sp = services.BuildServiceProvider();
IOptions<OrderOptions> iop = sp.GetService<IOptions<OrderOptions>>();
OrderOptions op = iop.Value;

services.AddOrderService(options => {
                options.Value.OrderType = op.OrderType;
                options.Value.CutOffDate = op.CutOffDate;
            });

However, all that code is probably unnecessary. Once I've added an object to the IServiceCollection, as my previous Controller-based example showed, I can fetch objects from the IServiceCollection when I need them. Question: When do I need my OrderOptions object? Answer: When I create my OrdersService object.

Revising the constructor for my OrdersService object to accept the IOptions<OrderOptions> object gives me code like this:

public class OrdersService
{
  public DateTime CutoffDate { get; set; }
  public OrderTypes OrderType { get; set; }
  public OrdersService(IOptions<OrderOptions> iop)
  {
    CutoffDate = iop.Value.CutOffDate;
    OrderType = iop.Value.OrderType;
  }
}

Now, I don't need to retrieve the IOptions<OrderOptions> object anywhere at all. It will now automatically be passed to the OrdersService object when it's created.

That's a lot of options, so let me lay out the main line of working with appsettings.json, IConfiguration, IServiceCollection and IOptions:

  1. Add entries to the appsettings.json file
  2. Create a class with matching properties
  3. Use the IConfiguration object's GetSection method to retrieve an IConfigurationSection object
  4. Pass that to the IServiceCollection's Configure method to create an IOptions object
  5. Retrieve the IOptions object wherever you need it

That's not all that complicated. At least, not that complicated for .NET Core ... relatively speaking.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Full Stack Hands-On Development with .NET

    In the fast-paced realm of modern software development, proficiency across a full stack of technologies is not just beneficial, it's essential. Microsoft has an entire stack of open source development components in its .NET platform (formerly known as .NET Core) that can be used to build an end-to-end set of applications.

  • .NET-Centric Uno Platform Debuts 'Single Project' for 9 Targets

    "We've reduced the complexity of project files and eliminated the need for explicit NuGet package references, separate project libraries, or 'shared' projects."

  • Creating Reactive Applications in .NET

    In modern applications, data is being retrieved in asynchronous, real-time streams, as traditional pull requests where the clients asks for data from the server are becoming a thing of the past.

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

Subscribe on YouTube