ASP.net Core 2.2 configuration - asp.net-core

Coming from a webforms background, I'm trying to understand how configuration and environment translation works in .net core 2.2 MVC web apps. Gone are the web.config files and the ConfigurationSettings.AppSettings property. I'm finding the documentation a little unclear.
The documentation states I need to call AddJsonFile or AddXmlFile during application startup. Like this:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile(
"config.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>();
The project template I use already has the following logic:
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
My project has appsettings.json and appsettings.development.json files. When I put a breakpoint on the Startup method of the Startup class, I can inspect the configuration parameter and see the two json configuration files exposed as what looks to be a dictionary.
Questions
So do I have to explicitly call AddJSonFile, or is this actually done for me somehow by the framework?
How do I handle transforming configuration for different deployments?
What is the best way to access this configuration in a controller?

So do I have to explicitly call AddJSonFile, or is this actually done for me somehow by the framework?
This is done in the framework. Most notably the "DefaultBuilder" adds in both appsettings.json and appsettings.{Environment}.json, among other things. https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webhost.createdefaultbuilder?view=aspnetcore-2.2
How do I handle transforming configuration for different deployments?
You need to set the Environment variable on the host machine (This is the easiest way althought here are other ways to do it). So for example if you set the environment to be Production, then it will first load appsettings.json, then it will load appsettings.Production.json and override the default settings. More info here : https://dotnetcoretutorials.com/2017/05/03/environments-asp-net-core/
What is the best way to access this configuration in a controller?
There are two ways. You can use the Options pattern built into the framework : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.2
Or you can use good old fashioned POCO's (https://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/).
All you need to do there is load out your configuration in your ConfigureServices method and bind it to a singleton :
services.AddSingleton(Configuration.GetSection("myConfiguration").Get<MyConfiguration>());
Then you can simply request it in your controller via DI:
public class ValuesController : Controller
{
private readonly MyConfiguration _myConfiguration;
public ValuesController(MyConfiguration myConfiguration)
{
_myConfiguration = myConfiguration;
}
}

Related

ILogger in Asp.net Core and Serilog

I have a question,
Is there any concern if I use ILogger in Serilog on behalf of Microsoft logger?
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Log.Logger);
}
Then use from ILogger in Serilog namespace.
_logger.Error(exception, "Error", exception.Message, exceptionId);
What is your idea?
Microsoft.Extensions.ILogger is an abstraction that decouples your application code (the code that writes log entries) from the underlying logging framework.
Using this abstraction brings the benefit, that you could easily change the Logging Framework underneath (e.g. replace Serilog with NLog) and don't have to update all references in your application code.
Also using Microsoft's abstractions also allows you to configure logging of your application code and logging of Microsoft SDKs you might use at a single place.
The downside of abstractions is that you have to aggree to a common minimum interface provided by all logging frameworks. It's not so easy to use Framework-Specific features this way.
So most of the time i would advise using the abstraction.
If you have very specific features from Serilog you would like to interact with you could think about using ILogger from Serilog directly.
However you can configure serilog in the provider registration as well to a high degree and probably get the best of both worlds.
You would configure Serilog factory interface to be used instead of built-in Logger factory for creating ILogger
First in program.cs, add the Serilog ILoggerFactory to your IHostBuilder with UserSerilog() method in CreateHostBuilder():
public static IHostBuilder CreateHostBuilder(string[] args) =>
new HostBuilder()
.ConfigureHostConfiguration(builder => { /* Host configuration */ })
.ConfigureAppConfiguration(builder => { /* App configuration */ })
.ConfigureServices(services => { /* Service configuration */})
.UseSerilog(); // <- Add this line
}
How the library works behind the scenes
On the face of it, completely replacing the default ASP.NET Core logging system to use a different one seems like a big deal. Luckily, thanks to the use of interfaces, loose coupling, and dependency injection, the code is remarkably simple! The whole extension method we used previously is shown below:
public static class SerilogHostBuilderExtensions
{
public static IHostBuilder UseSerilog(this IHostBuilder builder,
Serilog.ILogger logger = null, bool dispose = false)
{
builder.ConfigureServices((context, collection) =>
collection.AddSingleton<ILoggerFactory>(services => new
SerilogLoggerFactory(logger, dispose)));
return builder;
}
}
The UseSerilog() extension calls the ConfigureServices method on the IHostBuilder and adds an instance of the SerilogLoggerFactory as the application's ILoggerFactory. Whenever an ILoggerFactory is required by the app (to create an ILogger), the SerilogLoggerFactory will be used.
for more Information check this Link
"The downside of abstractions is that you have to aggree to a common minimum interface provided by all logging frameworks. It's not so easy to use Framework-Specific features this way."
I think we can always create extension methods to overcome this downside.

Is there a way to deactivate IIS integration in Net Core 3.X app without having to reproduce the code from CreateDefaultBuilder

In version 3.0 and up of Net Core a web application can be setup this way:
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration(config =>
{
var settingsFile = Environment.GetEnvironmentVariable("APP_SETTINGS");
if (!string.IsNullOrWhiteSpace(settingsFile))
{
config.AddJsonFile(settingsFile, optional: false);
}
});
webBuilder.UseStartup<Startup>();
});
While using CreateDefaultBuilder very convenient and abstracts a lot of boilerplate, it also forcibly enables IIS integrations for the app. In my case, I want to run the app using raw Kestrel without IIS proxying requests.
Is my only choice to extract all the boilerplate from CreateDefaultBuilder() if I want to not use the IIS integrations, or can I somehow overwrite this? I've been studying the source code for this methods and I can't see a way to solve this.
Do you know of an easier method to achieve this other than having to write all the setup code myself?

ASP.NET Core 2: In `Startup()`, how do I specify additional arguments to choose the right configuration?

We are building a new ASP.NET Core app to deploy to a few different environments that require different Configuration options. For example, Azure, AWS, and local for development. Each needs different configuration settings. We already use the common:
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
which handles debug vs staging vs prod, but we have a second dimension to it, so we also want a:
.AddJsonFile($"appsettings.{DeploySite}.json", optional: true)
or something like that. We could pass in the DeploySite on commandline or environment variable or whatever is convenient, but nothing like that seems exposed in the IHostingEnvironment offered in the Startup method.
How is this best accomplished?
In ASP.NET Core 2+ this is done in Program.CreateWebHostBuilder instead of Startup. We do something almost identical in our deployments, requiring an appsettings.<name>.json file for configurations. We do it by passing in a command line parameter in the form of --App:InstanceName <name>. Then set it on launch:
public static void Main(string[] args)
{
IWebHost host = CreateWebHostBuilder(args).Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((hostContext, config) =>
{
IConfigurationRoot commandConfig = config.Build();
config.AddJsonFile($"appsettings.{commandConfig["App:InstanceName"]}.json", optional: true, reloadOnChange: true);
});
}

How do I learn how to configure CreateWebHostBuilder to serve Razor pages, and other addins like Websockets?

I have a default website that runs well on MacOS
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
However I need to run Websockets, and MVC code, however the following Kestrel configuration doesn't allow me to view index.html on port 500001
How do I (learn how to) properly configure this for MVC and Websockets on MacOS?
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost
.CreateDefaultBuilder(args)
// Increase Shutdown timeout to accomodate background tasks.
//.UseShutdownTimeout(TimeSpan.FromSeconds(10))
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel((hostingContext, options) =>
{
if (hostingContext.HostingEnvironment.IsDevelopment())
{
options.Listen(IPAddress.Loopback, 50001, listenOptions =>
{
listenOptions.UseHttps("localhost.p12", "1234");
});
}
});
Configuring ASP.NET itself is done inside your Startup class (which you've already specified with UseStartup<Startup>() and not inside your Program class.
Open your Startup.cs file and ensure you have:
UseStaticFiles() to serve static files like index.html (index.html is not a Razor View for ASP.NET MVC nor a Razor Page, as those have the extension .cshtml and are generally named after their corresponding controller actions in TitleCase.
UseMvc() to use the ASP.NET Core MVC system with Controller and Razor View support.

How to get DotNet Core ApplicationName set by Environment Variable

According to the .NET Core documentation, I should be able to set the application name using an environment variable.
Environment variable: ASPNETCORE_APPLICATIONKEY
I am not seeing this to be the case. I added the WebHostDefaults.ApplicationKey setting to the Program.cs but I am still unable to override it with an environment variable.
private static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("hosting.json", true)
.AddEnvironmentVariables("ASPNETCORE_")
.AddCommandLine(args)
.Build();
return WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((context, builder) => { builder.ClearProviders(); })
.UseConfiguration(config)
.PreferHostingUrls(true)
.UseStartup<Startup>()
.UseSetting(WebHostDefaults.ApplicationKey, "CustomApplicationName")
.Build();
}
In startup.cs I am only seeing "CustomApplicationName" rather than the environment variable.
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
Configuration = configuration;
Log.Information($"Startup of application {hostingEnvironment.ApplicationName} in Environment Mode {hostingEnvironment.EnvironmentName}");
}
}
I have tried using double underscore in the environment variable name as well.
I am running on Mac OS.
As mentioned in other answers, the correct environment variable name is ASPNETCORE_APPLICATIONNAME, and it is documented here. However, it will not work, even as of .NET Core 3.1. There is a GitHub issue that describes the details of this bug, but essentially, the code inside the UseStartup<>() method sets ApplicationName back to its default value, which is the name of the assembly.
Even if you could override it back using the UseSetting() method, I wouldn't do it, based on the warnings in the discussion thread at this related GitHub issue. The safest bet for now seems to use your own separate environment variable.
I suspect this is something that the documentation "invented" and isn't actually implemented.
ASP.NET Core is hosted on github. I did a search. The only place where ASPNETCORE_APPLICATIONKEY shows up is in the documentation itself. The only issue/PR where it comes up is https://github.com/aspnet/Docs/pull/7493 which is the commit that added this environment variable to the docs and includes this insightful statement:
Did I just make up ASPNETCORE_APPLICATIONKEY? Is that a thing?