IMPORTANT: This is NOT a duplicate of How to get IConfiguration from within Main? !
I have read that topic, I have read answers and comments and NO, my case is completely different.
The exact point when I need my configuration is before invoking UseUrls from IHostBuilder.
This is invoked from Main. My URLs are defined in appsettings.json file, and now I get the configuration like this:
var configuration = (
new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))
.Build();
But this seems wrong. I mean the framework somehow loads the configuration and injects it to the services. However, the point I need it is before the services are even configured.
I also have a special execution mode for my Main - when import command argument is used, my data context is initialized (again, using configuration from appsettings.json) to import data from external database. When this happens, no web server is started at all, the program behaves like a console application. So again, dependency injection is not available, I instantiate the configuration as described. This is well, OK, because the initialization is done only once.
However, my "hack" initializes the IConfiguration and then the framework does it again. This looks like a code smell and I wonder how to fix this.
Here's my CreateHostBuilder method:
private static IHostBuilder CreateHostBuilder(string[] args) {
var defaultBuilder = Host.CreateDefaultBuilder(args);
var configuration = (new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)).Build();
return defaultBuilder.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseStartup<Startup>().UseUrls(configuration.GetValue<string>("InternalSiteUrl"));
});
}
There is a better way, isn't it?
Related
I've configured nlog to write logs to database, giving host for connection string from appsettings.json using config layout drawer. But there are some log calls inside Startup.ConfigureServices and Startup.Configure. So, when the configuration is not ready, nlog in this moment tries to write to database. It takes empty host (config is still not ready!), and in the nlog's own log file I see the error of failed logging because of empty host.
So, the question: is it the way not to log to database target utill the config will be ready?
I'm guessing the issue is that you start using NLog before having loaded MEL-Configuration.
The "solution" is to perform early load and setup the appsettings.json:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
ConfigSettingLayoutRenderer.DefaultConfiguration = Configuration;
// Loads NLog.config one more time
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
See also: https://github.com/NLog/NLog.Extensions.Logging/issues/265
NLog 4.7.1 and NLog.Web.AspNetCore ver. 4.9.3 will make it possible to do this:
var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
See also: https://github.com/NLog/NLog.Web/pull/540
I used the config in Startup.cs file (and in also in other).
app.UseCors(options =>
{
options.WithOrigins(Configuration["AllowedCors"]).AllowAnyMethod().AllowAnyHeader();
});
I publish my code (to deploy),
and sometimes, I have to changes my appsettings.json.
But, seem like it's still getting old value. And I always need to restart my app to getting new value work.
Does anyone have anny idea?
You should use reloadOnChange: true in your startup file.
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
Configuration = builder.Build();
}
You can use IOptionsSnapshot instead of IOptions for inject settings in the constructor, after set reloadOnChange: true like this answer.
private readonly BranchSettings _settings;
public Constructor(IOptionsSnapshot<BranchSettings> settings)
{
_settings = settings?.Value;
}
I'm unsure if this is what you are asking, but when you change appsettings.json on a already running deployment, to load new values you have to restart that deployment.
EDIT: Seems like I was mistaken, Ali gave a simple solution without restarting the application.
In this thread I was able to setup my simple console application using ASP.NET CORE's configuration system.
The code is as simple as:
static void Main(string[] args)
{
string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: false)
.AddEnvironmentVariables();
config = configuration.Build();
var serviceProvider = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddDiscoveryClient(config)
.BuildServiceProvider();
Console.WriteLine(config["Test"]);
Console.Read();
}
However, since the application does not use IApplicationBuilder, I cannot invoke the .UseDiscoveryClient() method. I end up receiving an error on .AddDiscoveryClient(config):
"Discovery client type UNKNOWN, check configuration"
Is there a work around this? We would like to experiment using console applications with our Spring Cloud Config server. If there is no way to do it with Steeltoe, feel free to inform with other libraries that do.
The extension methods AddDiscoveryClient and UseDiscoveryClient are for use with Steeltoe service discovery. The error message you're seeing is due to Steeltoe not knowing which type of service registry your app should be a client of (ie: "client type UNKNOWN").
You only wish to access Spring Cloud Config server, so you don't need either of those methods. You can add the ConfigServerConfigurationProvider to you your config builder with .AddConfigServer.
you should add "appName" and "hostname" in the appsetting.json under instance
I'm using ASP.NET Core 2.0 and I have configuration code like this in the Main method:
public static void Main(string[] args)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment ?? "Production"}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
}
I have the reloadOnChange set to true, and in my controller I am using IOptionsSnapshot;
public HomeController(ILogger<HomeController> logger, IOptionsSnapshot<AppSettings> options)
But when I modify the values in my appsettings.json, I have to restart my app every time or the changes are not being picked up just by refreshing the browser. What am I doing wrong?
I've tried to run the app both with console and IIS Express; I've also tried IOptionsMonitor, same thing. What is the difference between IOptionsMonitor and IOptionsSnapshot?
As mentioned in the documentation, just enabling reloadOnChange and then injecting IOptionsSnapshot<T> instead of IOptions<T> will be enough. That requires you to have properly configured that type T though. Usually a configuration registration will look like this:
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
However, looking closer at your code, it does not seem that you are using the new ASP.NET Core 2.0 way of configuring your program. The configuration is now part of dependency injection, so you will set it up as part of the WebHostBuilder, using ConfigureAppConfiguration. That could for example look like this:
public static IWebHost BuildWebHost()
=> new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((builderContext, config) =>
{
IHostingEnvironment env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>()
.Build();
If you are using the default builder using WebHost.CreateDefaultBuilder(), then you don’t even need to do this, as the configuration is then automatically set up like that with reloadOnChange activated.
The difference between IOptionsSnapshot and IOptionsMonitor is that the IOptionsSnapshot will just give you a snapshot of the options at the time the IOptionsSnapshot<T> object is being constructed.
That’s why the usage is exactly the same as with IOptions<T>: You inject it in the constructor and then store the options.Value in the instance to access the options later. At that point, that object is fixed and will never change. It’s just that the IOptionsSnapshot<T> is registered as a scoped dependency instead of a singleton dependency like IOptions<T>, so it gets the chance to get the current configuration values on every request instead of just once.
The IOptionsMonitor<T> however is a singleton service that allows you to retrieve the current configuration value at any given time. So it is especially useful for singleton services that need to get the current configuration whenever they need it. In addition, the options monitor offers a push mechanism to get notified of configuration changes by the configuration sources. That way, you can explicitly handle configuration changes.
Options snapshots are designed to be used for transient or scoped dependencies, so you will be fine just using those most of the time. Only in rare occasions when you have to use a singleton service that also needs to have the most updated configuration, you should have the need to use an options monitor. In those cases, note that just switching from snapshots to a monitor will not be enough. Usually, you will have to handle changed configuration in some way (for example clean up state, clear caches etc.). So you should always think about whether you actually need reloadable configuration for everything or if just restarting the application isn’t a viable alternative.
In my Startup.cs, I have the following boilerplate code:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
What I want to do is access and re-use the already-built Configuration object with a Controller elsewhere in my project without having to build a new configuration object over there. How can I do that or is it even possible?
You have a few alternatives:
Store the configuration object in a global variable.
Pro: simple.
Con: creates coupling and makes everyone aware of the Configuration object. No DI. No type safety.
Add the configuration object to the DI container and consume it where you need it.
Pro: decoupling. Supports DI.
Con: you make other components aware of configuration. No type safety.
Create an options object either by using IOptions or by using ConfigurationBinder and add it to the container.
Pro: nice architecture where each component knows only about the options object. Supports DI. Type safe.
Con: more code.
For a Hello World size app, option #1 works just fine. For a real world application I would definitely consider #3.