IConfiguration does not retrieve values from appsettings.json but from Azure Key Vault - asp.net-core

Inside the Program.cs file of a .NET Core 3.1 Web API i got the following:
public class Program
{
private static string _someValue;
public Program(IConfiguration configuration)
{
_someValue = configuration["SOME_VALUE"]
}
inside the appsettings.json i got:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"SOME_VALUE": "value"
}
The problem is, i am not able to get the value "SOME_VALUE" from the appsettings.json. But i got this same value inside an Azure Key Vault and for some reason it gets the values from there. Can someone explain why that is the case?

Configuration providers are executed in a specific order
In your case, the Azure KeyVault will be at the top (or, to be more precise it is between AddUserSecrets and AddEnvironmentVariables)
See docs
Order configuration providers in code to suit the priorities for the underlying configuration sources that the app requires or use different variable name

Related

Set Log Levels for Microsoft.* , System, and AspNet when using Serilog

I had always imagined that
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
would set my project code's log level to Debug, and make Microsoft.* namespaces log at Information level or higher. It seems not. With this config, AspNetCore infrastructure logs at Debug level.
How do I target Microsoft.* to not use the Default level?
I'm using Serilog, but the injected Loggers are all Microsoft.Extensions.Logging.ILogger so I expected Microsoft.Extensions.Logging config to kick in.
Is this a mistaken assumption?
Yes, the assumption that Serilog will respect the Logging.LogLevel section when used with Extensions.Logging is wrong. It doesn't.
To read serilog levels from config, some options are:
use https://github.com/serilog/serilog-settings-configuration and use the config structure they give, especially the section at MinimumLevel, LevelSwitches, overrides
For those who do Serilog config in code, use something like this:
var defaultLogLevel = configuration.GetLogLevel("Default");
var aspNetCoreLogLevel = configuration.GetLogLevel("Microsoft.AspNetCore");
var microsoftLogLevel = configuration.GetLogLevel("Microsoft");
var logger = new LoggerConfiguration()
.MinimumLevel.Is(defaultLogLevel)
.MinimumLevel.Override("Microsoft.AspNetCore", aspNetCoreLogLevel)
.MinimumLevel.Override("Microsoft", microsoftLogLevel)
// ... etc ...
// ...
static LogEventLevel GetLogLevel(this IConfiguration configuration, string #namespace, string fallbackLevel = "Information")
{
return Enum.Parse<LogEventLevel>(configuration["Logging:LogLevel:" + #namespace] ?? fallbackLevel);
}
but the in-config approach for minimum levels has the advantage that you can make use of reloadOnChange

Console logger with json formatter does not serialize objects

I try to use default .NET core logging tools without any third party libs. So, question.
Does console logger with Json formatter support json serialization of objects?
I have the following config in appsettings
"Logging": {
"Console": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "json",
"FormatterOptions": {
"SingleLine": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
The following line
logger.LogDebug("RequestId:{requestId} ResponseInfo: {#response} ", requestName, response);
produces the output with string representation of #response object, internally it still invokes ToString(), but docs in Caution section says that
The logging infrastructure itself already manages the serialization of log messages, so if you're to pass in a log message that is already serialized—it will be double serialized
docs
And this point me that it should serialize object.
I think the build-in ILogger don't support this. That's why we can try the new System.Text.Json source generator.
The new System.Text.Json source generator can improve logging performance.
I think we also can choose third-party package, like Serilog. That should be more easy to do this.

ASP.NET Web API not logging exception in application insights when deployed in Azure

I have enabled application insights logging in my ASP.NET Web API project which is done from a class implementing IExceptionLogger. In debug mode it's logging exceptions to application insights using TrackException().
But when the same code is deployed in an Azure App Service, it's not sending exception to application insights, but it's able to send data using traces.
After trying lot of different ways (flush also), I have added one method which will log exception in SharePoint list, after that Web API is also able to send data to Application Insights. When commenting out the SharePoint code, the Web API stopped sending exception to Application Insights.
I am not sure whether its happening due to some extra time that TelemetryClient is getting due to SharePoint code.
Anyone faced this issue?
Here is the Microsoft doc link which I have followed: LINK
Make sure you have added the Application Insights logging level in appsettings.json
{
"Logging": {
"ApplicationInsights": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ApplicationInsights": {
"ConnectionString": "< App insights connection string>"
}
}
After adding the log level of application insights I can able to see the custom logs and exceptions.
Here you can see the security level to verify the logging level
Refer here

How to use publish profile to change a value is appsettings.json

I am using .NET Core 3.0 ASP.net and my appsettings.json looks like this
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"Test":false
}
Is there any way that I could configure a certain publish profile (pubxml) to change the value of Test to either true or false ?
Yes, it's very easy. Currently, you are most likely using the Debug solution configuration for local development. Let's assume you also have Release as solution configuration and want to change certain settings upon publishing to Azure / with your publish profile.
Assuming you are using .NET Core 3.1 (also works with 2.x but the syntax is different), you can this code:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseEnvironment(Environment);
});
public static string Environment
{
get
{
string environmentName;
#if DEBUG
environmentName = "development";
#elif RELEASE
environmentName = "production";
#endif
return environmentName;
}
}
Also create a appsettings.product.json where you override specific values.
When you use your publish profile, simply select Release as solution configuration in the publish dialog. That way, you application will load appsettings.json for default values which will be overwritten by values in appsettings.production.json.
You can read more details in the docs.

EF Core 3: CLI "Migrations add" fails on "No database provider has been configured for this DbContext"

The problem:
The dotnet ef Migrations add MyNewMigration command fails with:
No database provider has been configured for this DbContext. A
provider can be configured by overriding the DbContext.OnConfiguring
method or by using AddDbContext on the application service provider.
If AddDbContext is used, then also ensure that your DbContext type
accepts a DbContextOptions object in its constructor and
passes it to the base constructor for DbContext.
There are many SO posts regarding this issue, and I have read most of them. The one that seems to have the exact same problem is here:
EF Core unable to run commands. Error: No database provider has been configured for this DbContext
However, the issue was never resolved. Here are some bullets that sums the investigation up and further down, details about the steps.
We had initially just hardcoded the connection string in the DbContext OnConfiguring method, and then the Migrations command worked well.
We then proceeded to use a static method, that read the appsettings.json file. This worked when running the app, but it did not work when running the Migrations add command, because the connectionString we fetched from the static class, always returned null.
We then moved to use dependency injection, like everyone everywhere suggests doing. in the Startup.cs, we use something like services.AddDbContext<MyDbContext> and in the MyDbContext we have a constructor like public MyDbContext(DbContextOptions<MyDbContext> options) : base(options). The constructor is called, the connectionstring is there, app can run, but Migrations Add fails with same error message as above.
I then tested removing the default empty constructor, just keeping the MyDbContext(DbContextOptions<MyDbContext> options) constructor. The error message from the command was then "Unable to create an object of type 'MyDbContext'."
Since I run the commands from my Data project (where entities, dbcontext etc exists), I tried adding a startup path to the command, like dotnet ef Migrations add MyMigrationStuff --startup-project C:\Git\MyProject\MyProject.Api, still without default/empty constructor. This time, the error message was simply Build failed. I then reinstated the empty construtor, ran same command again: then I get the same error as above.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ... other stuff
string cString = configuration["ConnectionStrings:MyDb"]; // cString is correct and valid!
services.AddDbContext<MyDbContext>(options => options.UseMySql(cString, mySqlOptions => mySqlOptions
.ServerVersion(new ServerVersion(new Version(5, 0, 17), ServerType.MySql))));
// services.AddDbContext<MyDbContext>(); // without DI as in case 1 and 2 above
}
cString looks correct when running app:
MyDbContext:
public class MyDbContext : DbContext
{
// DbSet etc...
public MyDbContext() : base()
{
}
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
// If I break here, the options has two Extensions, looks ok, see below.
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
}
This is the options in the MyDbConstructor:
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"ConnectionStrings": {
"MyDb": "Server=localhost;Port=3308;Database=mydb;User=root;Password=root;"
}
}
I am running ASP.NET Core 3.1.101, EF Core 3.1.1, visual studio 2019.
you need to install MySQL provider using this cmd:
dotnet add package MySql.Data.EntityFrameworkCore
then add this code to your DBContext class constructor:
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseMySQL(Configuration.GetConnectionString("MyDb"));
}
finally, add this line into your startup.cs class instead of what you did put:
services.AddDbContext<MyDbContext>();