I have a. NET core console app that implement IHostedService and a reference to another project with a DbContext definition.
This is the configuration of DbContext in console app:
IHost host = new HostBuilder()
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configHost.AddCommandLine(args);
})
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.SetBasePath(Directory.GetCurrentDirectory());
configApp.AddEnvironmentVariables(prefix: "ASPNETCORE_");
configApp.AddJsonFile($"appsettings.json", true);
configApp.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true);
configApp.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<MyAppDbContext>(options => options.UseNpgsql(hostContext.Configuration.GetConnectionString("DefaultConnection")));
services.AddHostedService<ApplicationLifetimeHostedService>();
})
.Build();
Now, in the OnStarted() method of ApplicationLifetimeHostedService I have:
using (var _context = new MyAppDbContext())
{
...
_context.SaveChanges();
}
Why MyAppDbContext take the connection string value from OnConfiguring method of dbcontext definition class (hard-coded, generated from scaffolding), and not from appsettings.{ASPNETCORE_ENVIROMENT}.json ()?
Thank you in advance!
Based on your configuration, currently the IHostBuilder is for non web applications and simulates a generic configuration, eventually this will replace the IWebHostBuilder. However, you do not need those... In your instance you would be better off with CreateDefaultBuilder.
Host Configuration
App Configuration
Both are provided by default, with more granular control. The primary item is the default services provided by the builder and what they compile or build.
To directly answer your issue though, in your top line, you are missing the following:
var host = new HostBuilder()
.ConfigureHostConfiguration(configuration =>
{
// For brevity, removed some.
configuration.AddJsonFile("appsettings.json", false, true);
}
That is why your appsettings.json is not working. The ConfigureHostConfiguration will carry through to the ConfigureAppConfiguration.
Host configuration automatically flows to app configuration
(ConfigureAppConfiguration and the rest of the app).
No providers are included by default. You must explicitly specify
whatever configuration providers the app requires in
ConfigureHostConfiguration, including:
File configuration (for example, from a hostsettings.json file).
Environment variable configuration.
Command-line argument configuration.
Any other required configuration providers.
Related
In a console application, rather than building the IConfiguration and IServiceProvider manually, I'm trying to use the Host.CreateDefaultBuilder() process:
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddSingleton<Whatever>();
})
.Build();
I can get the configuration object after building the host. But what I'm looking for is a way to get the config object while still in the ConfigureServices body, so that I can bind a config section to the service provider.
Something like:
AccountConfiguration accountConfig = new();
config.Bind("AccountConfiguration", accountConfig);
services.AddSingleton(accountConfig);
// or
services.Configure<AccountConfiguration>(config.GetSection("AccountConfiguration"));
Is there a way to access the config object while still configuring the services? Or a good way of adding an object to the service collection after the host has already been built?
The first parameter of lambda passed to ConfigureServices is HostBuilderContext which exposes configuration property - IConfiguration Configuration:
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
IConfiguration config = context.Configuration;
// use config
services.AddSingleton<Whatever>();
})
.Build();
This is my first time working with Microsoft.Extensions.Logging.Log4Net.AspNetCore.
I have a WPF application where I add the Log4Net provider within the CreateDefaultBuilder in App method of App.xaml.cs. Immediately after, I want to write to the log file using LogInformation saying "Starting Application". From what I can tell, it does not seem like I can do this because of the way it has been added to the Dependency Injection container. I must either call a method that accesses the DI container in the parameter list or add Log4Net to the ServiceProvider and then retrieve the service that way. But, that does not seem right because I will effectively have Log4Net added to DI twice.
Is there a way to immediately access the DI container after configuring Log4Net so I can write to the log file?
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, appBuilder) =>
{
// Do some stuff here
}).ConfigureLogging(logBuilder =>
{
logBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
logBuilder.AddLog4Net("log4net.config");
})
.ConfigureServices((context, services) =>
{
Configuration = context.Configuration;
ConfigureServices(Configuration, services);
})
.Build();
ServiceProvider = Host.Services;
// How to access Log4Net here?
_ISomeLogger.LogInformation("Starting Application");
Update
I am using the following example: https://www.thecodebuzz.com/logging-using-log4net-net-core-console-application/
The solution is to access the Log4Net from the ServiceProvider right after building it:
logger = (ILogger<Program>)ServiceProvider.GetService(typeof(ILogger<Program>));
I have a .net 5 web application that uses Application Insights. I try to log into AI trace by using ILogger<>. However: When analyzing the "traces" - Content in AI on Azure the logs are not shown.
Part of StartUp:
services.AddLogging(loggingbuilder =>
{
loggingbuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
loggingbuilder.AddApplicationInsights();
});
services.AddApplicationInsightsTelemetry();
The constructor of the class that should do the logging injects ILogger and AppInsights via dependency injection:
public ImportService(ILogger<ImportService> log, TelemetryClient telemetryClient)
{
_log = log;
_telemetryClient = telemetryClient;
}
Inside the method I have the following two logging attempts:
public async Task<Customer> UpdateCustomerByEmail(string email)
{
_telemetryClient.TrackTrace("From Telemetry");
_log.LogWarning("From Log");
[...]
}
While the first one ("from Telemetry") ends up correctly in AI-traces, the second one ("From Log") never shows up there.
The instrumentationkey is stored in the appsettings (and obviously correct because the telemetryClient-Track is working)
Might this documentation be relevant for you? Adding the following code to program.cs worked for me:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddApplicationInsights("<instrumentationKeyHere>");
logging.AddFilter<Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider>("", LogLevel.Information);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
The nuget package Microsoft.Extensions.Logging.ApplicationInsights must also be installed.
In your case it might be sufficient to simply provide the Instrumentation Key as a paramter to the AddApplicationInsights function.
As stated in the documentation "This code is required only when you use a standalone logging provider. For regular Application Insights monitoring, the instrumentation key is loaded automatically from the configuration path ApplicationInsights: Instrumentationkey."
This might explain why regular monitoring works, but not for logging.
We are currently converting a web app single-tenant to multi-tenant.
We are looking to overwrite the data contained in appsettings.json based on the TenantId (retrieved from the header, or query string, or RabbitMQ Header).
The "Tenant Identification" services work well. We got an ITenantResolverService which return a TenantId.
Currently, our IOptions are overwritten by appsettings.json then appsettings.Development.json then finally, by the environment variables.
public Startup(IHostingEnvironment environment)
{
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", true)
.AddJsonFile("appsettings.local.json", true)
.AddJsonFile($"k8s-mount/appsettings.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
We are looking to add, before environment variable, an override based on the TenantId. Eg: appsettings.tenantId.json and at some point, from a JSON file in Vault.
Where should we get started? We've seen a lot of articles regarding "hard-coded" value per tenant. (eg: https://michael-mckenna.com/multi-tenant-asp-dot-net-core-application-tenant-specific-configuration-options)
But nothing seems to fulfill the requirement,
Sorry for not getting here earlier, but here what you can try. Use files configuration provider and put your tenants configuration in files named by tenant. ASP.NET runtime will watch the files for you. Initially this mechanism was built to use file per config value, so if you want configuration objects you will have to deserialize them.
Here is how to add file configuration provider:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(confBuilder =>
{
confBuilder.SetBasePath(Directory.GetCurrentDirectory());
var path = Path.Combine(Directory.GetCurrentDirectory(), "tenant-configs");
confBuilder.AddKeyPerFile(path, true);
})
.UseStartup<Startup>();
You can find full sources here: https://github.com/MichaelSL/asp.net-multitenant-configs
I am starting to work on an ASP.NET Core 2.0 Web Api. I added 2 secrets to the secrets.json file and am trying to read them in through the Configuration property in my Startup file. Each time I called I try to get the value from the Configuration variable, it returns null. An example of how I am reading this from the secrets.json is shown below.
public void ConfigureServices(IServiceCollection services)
{
var secret = Configuration["TestSecret"];
My secrets.json file looks like:
{
"TestSecret": "SecretValue"
}
I have also tried to retrieve the value by using:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection secret = Configuration.GetSection("TestSecret");
var value = secret.Value;
This returns a section that corresponds to the TestSecret section, but the value in the IConfigurationSection is also null.
I have tried to install the Microsoft.Extensions.Configuration.UserSecrets NuGet package, but this hasn't helped. Am I missing a package I need to install or is there an alternate way to retrieve this value?
If I need to provide any more information to help solve this issue, please ask in the comments. I will try to provide as much information as possible to help solve this issue.
In general you use a file called "appSettings.json" file for storing all json values like that. This file doesn't need to be manually added in 2.0. Unless there is a specific reason for having multiple json files I would recommend doing this as it allows you to have all your application specific settings in the one place
it is possible to manually add a .json file though.
for asp.net Core 2.0 in your Program.cs in the BuildWebHost method
you add in the following
.ConfigureAppConfiguration((WebHostBuilderContext, ConfigurationBuilder) =>
{
ConfigurationBuilder
.AddJsonFile("Secrets.json", optional: true);
ConfigurationBuilder.AddEnvironmentVariables();
})
depending on the setup the entire method should look like
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((WebHostBuilderContext, ConfigurationBuilder) =>
{
ConfigurationBuilder
.AddJsonFile("Secrets.json", optional: true);
ConfigurationBuilder.AddEnvironmentVariables();
})
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
To get a value from in your ConfigureServices method you use
var testSecret = Configuration.GetSection("Secrets")["TestSecret"];
this is how the Secrets.Json file should look
{
"Secrets":
{
"TestSecret": "SecretValue"
}
}
Did you configure to use secrets in the Startup method?
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder();
if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}
Configuration = builder.Build();
}
Read more here
For me it was null because I was playing around with other ASPNETCORE_ENVIRONMENT values and the default logic is apparently dependent on this being "Development"... which makes basic sense given that secrets.json is entirely local dev scenario.
As a related aside, for us there's mild heartburn that "Development" could mean either cloud runtime dev or raw local dev so we're forced to create yet another key/value to represent local dev because ASPNETCORE_ENVIRONMENT = Development drives specific logic we don't want to lose.