There isn't that much difference between the two (how it works in a dotnet-core "app" vs how it works in aspnet-core).
Now, I am going to assume that you don't simply want to console.write the values out, but you are eventually are going to want to have access to them inside of some real code....the below uses "UserController" as the code that needs access to your config values.
Ok, the big thing with dotnet core is you INJECT configuration (usually a POCO class) (see IOptions later in this answer for more options) into your code that needs the configuration. You do not "grab a value from app.config or web.config" like you may have done in dotnet framework.
Please re-read that and use it to change your thinking from dotnet-framework. I cannot emphasize this enough. Your "real code" that needs config values should not go out and read a file....you should INJECT the poco (or IOptions<MyPoco>) into your class.
So this is a typical setup that I do in a dotnet core console application.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public static class Program
{
public static int Main(string[] args)
{
try
{
/* look at the Project-Properties/Debug(Tab) for this environment variable */
string environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
Console.WriteLine(string.Format("DOTNET_ENVIRONMENT='{0}'", environmentName));
Console.WriteLine(string.Empty);
IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environmentName}.json", true, true)
.AddEnvironmentVariables();
/* pay attention to the "true, true" arguments above, they reflect optional: true, reloadOnChange: true .. "optional" being the important one here */
IConfigurationRoot myconfig = builder.Build();
IServiceProvider servicesProvider = BuildDi(myconfig );
using (servicesProvider as IDisposable)
{
/* now do something with your properly composed IoC container */
/* maybe something like this...*/
IUserController uc = servicesProvider.GetRequiredService<IUserController>();
/* UserController will have the email-config injected into it, because you did the IoC container setup correctly in "BuildDi" method .. later in this answer */
Console.WriteLine("Press ANY key to exit");
Console.ReadLine();
}
============================
So, what is that line
.AddJsonFile($"appsettings.{environmentName}.json"
all about?
First, it is OPTIONAL. You do not have to use it. But I am including it in this answer to provide a holistic answer. (if you don't want to use it, just remove or comment-out the code-line '.AddJsonFile($"appsettings.{environmentName}.json", true, true)'.
Second, is it a best practice? Hmm. For Development is is not so bad. But do you want to have your production config-values that are SECRETS in your .json file? That discussion is outside the scope of the original question and this answer.
But if you (decide to) use it.
There is a magic ENVIRONMENT variable to drive things.
One of the few places I found a different name for .net core console applications is here:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-3.1
So for asp.net core apps, I set
ASPNETCORE_ENVIRONMENT
for dotnet core console apps, I set
DOTNET_ENVIRONMENT
============
So with the above, you must have at least a "appsettings.json" file.
But you can optionally have these files.
appsettings.Development.json
appsettings.Staging.json
appsettings.Production.json
You can read more about that stuff here: https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/
Now, how do you get those values that are in .json files into your code that needs them.
In dotnet-core, configuration poco's are usually injected into the classes.
https://medium.com/@dozieogbo/a-better-way-to-inject-appsettings-in-asp-net-core-96be36ffa22b
The above shows a good example...
=======================
public class EmailConfig {
public string Username { get; set;}
public string Password { get; set;}
}
=======================
public class UserController : IUserController
{
private readonly EmailConfig _emailConfig;
public UserController(EmailConfig emailConfig)
{
_emailConfig = emailConfig;
//_emailConfig.Username
//_emailConfig.Password
}
}
and the .json will be appended with your extra custom configuration section/area......following the same example, the below shows a default .json file...BUT WITH "Email" section added in.
"ConnectionStrings": {
"DefaultConnection": ""
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"Email": {
"Username": "peanut",
"Password": "butter"
}
=======================
=======================
private static IServiceProvider BuildDi(IConfiguration myconfig)
{
////setup our DI
IServiceCollection servColl = new ServiceCollection()
.AddLogging(); /* do any other stuff here that is extension method */
EmailConfig emailConfig = new EmailConfig();
myconfig.GetSection("Email").Bind(emailConfig);
/* you may want to check for null here on emailConfig */
//Create singleton from instance..and push to the IoC container
servColl.AddSingleton<EmailConfig>(emailConfig);
/* not shown here.. but you'll need to add IUserController,UserController as well */
ServiceProvider servProv = servColl.BuildServiceProvider();
return servProv;
}
and germane csproj values as well.
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.*" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.*" />
</ItemGroup>
Now BONUS material
https://medium.com/@kmar.ayush/eli5-ioptions-vs-ioptionssnaphot-vs-ioptionsmonitor-fab1d7e26a75
Or internet search "ioptions vs ioptionssnaphot vs ioptionsmonitor"
You can also setup your POCO configurations with one of the above "Option"'s.
IOptions<T>
When using this, the values will always be same for the lifetime of the application. Changing the value of the appsettings file while the application is running will not make any difference to the value when using IOptions. In other words it is in Singleton scope.
IOptionsSnapshot<T>
Using this will always give you the most up to date values in your config file for every request and will remain the same for the during of your request.
IOptionsMonitor<T>
Using the monitor is almost like having real time access to your settings file. Each time you need the value from the config, it will read it and evaluate whatever is there and return that.
................
One of the advantages of injecting a Poco configuration object into your "real code" .. is that for unit tests .. EmailConfig can be "coded up" without backing .config/.json files). Aka, you can test your code without needing a json file..and run the test completely in-memory.
Finally, here is a small sample that shows how to actually "get at" that "default is 'Production'" value when no environment variables are set:
public static class Program
{
static void Main(string[] args)
{
string environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
Console.WriteLine(string.Format("DOTNET_ENVIRONMENT='{0}'", environmentName));
Console.WriteLine(string.Empty);
environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine(string.Format("ASPNETCORE_ENVIRONMENT='{0}'", environmentName));
Console.WriteLine(string.Empty);
/* both empty strings above */
Microsoft.Extensions.Hosting.HostBuilder builder = new Microsoft.Extensions.Hosting.HostBuilder();
builder
.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNETCORE_");
})
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
});
Microsoft.Extensions.Hosting.IHost hst = builder.Build();
/* do NOT do the below in real code..this is cheap trick to show EnvironmentName default */
Microsoft.Extensions.Hosting.IHostEnvironment env = hst.Services.GetService(typeof(Microsoft.Extensions.Hosting.IHostEnvironment)) as Microsoft.Extensions.Hosting.IHostEnvironment;
if (null != env)
{
Console.WriteLine(env.EnvironmentName);
}
}
and germane csproj parts
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.*" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.*" />
</ItemGroup>
Output:
DOTNET_ENVIRONMENT=''
ASPNETCORE_ENVIRONMENT=''
Production
WorkerServicetemplate instead? It can be run as a console or installed as a service/daemon. The nice thing about it is it comes withdependency injectionandconfigurationservices right out of the box