I'm writing this for those new to ASP.NET Core, based on what I see on StackOverflow. Most of what I've written here is already in the Docs, so check the links at the end of the post for detailed info.
The ASP.NET Core Web Host
Think of the web host as an instance of an application server that processes incoming web requests. We create one (usually in Program.cs
) as follows:
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.UseStartup<Startup>()
);
The CreateDefaultBuilder()
sets up the app's configuration. Source (v3.0.0). The app's configuration is represented by the IConfiguration
interface. (You can also manually create your own WebHostBuilder
instance if the defaults don't work for you, in which case you're responsible for setting up the app configuration.)
When the WebHost
is built using Build()
, your startup class's ConfigureServices()
is called. See WebHostBuilder.cs L187, WebHost.cs L110 and WebHost.cs L180.
Using IConfiguration in Startup
You can get an instance of IConfiguration
for use in your Startup class by constructor injection:
public class Startup
{
private IConfiguration Configuration { get; }
public Startup(IConfiguration configuration) // <-- the framework injects
// a valid instance
{
Configuration = configuration; // <-- and we save that
}
// rest of the class ...
}
The whole point of IConfiguration
is to provide a way through which the .NET Core app can read configuration from different sources: configuration files, environment variables, command line arguments, etc.
Using IConfiguration outside Startup
You can also inject an IConfiguration
instance almost anywhere:
public class EmailSender
{
private string ClientKey { get; }
private string ClientSecret { get; }
public EmailSender(IConfiguration configuration)
{
ClientKey = configuration["ClientKey"];
ClientSecret = configuration["ClientSecret"];
}
// rest of the class follows...
}
Let's just stop for a while and imagine an example: we're in a controller's action and need to send an email. We get an EmailSender
instance from DI, and use it to send the email. The EmailSender
gets email credentials from the IConfiguration
abstraction. It doesn't need to know where it's coming from: appsettings.json, command-line arguments, environment variables, azure vault storage– could be coming from anywhere and EmailSender
doesn't need to care about it.
There are still areas left wanting though:
- The configuration is not strongly typed: non-string values will need to be manually parsed.
- The email sender above requires
ClientKey
andClientSecret
to be defined using the exact configuration keys. SoEmailSender
is sort-of coupled with the way the actual configuration is set.
The solution to these is the Options Pattern.
Options Pattern
Think of this as an additional layer of abstraction. In the EmailService
example, we'd replace IConfiguration
with IOptions
. IConfiguration
knew how to get the right configuration from the right places for you, IOptions
knows how to get the relevant configuration from IConfiguration
for you.
Here's a simplified way to use it:
-
Group related settings together in your
appsettings.json
:
{ "Email": { // <-- settings grouped under "Email" "ClientKey": "xxx", "ClientSecret": "xxx" } }
-
Create a class to hold those settings:
public class EmailOptions { public string ClientKey { get; set; } public string ClientSecret { get; set; } }
-
Register the Options with the DI container:
services.Configure<EmailOptions(Configuration.GetSection("EmailOptions"));
-
Inject it into the service that needs it:
public class EmailSender { private string ClientKey { get; } private string ClientSecret { get; } public EmailSender(IOptions<EmailOptions> options) // <-- here { ClientKey = options.Value.ClientKey; ClientSecret = options.Value.ClientSecret; } // rest of the class follows... }
Follow this, and you won't see the need to inject IConfiguration
anywhere outside Startup
.
Top comments (0)