I have a question. Is it possible to read the appsettings.json File in the DbContext class of another class library? I tried to write an AppSettingsService.cs to read the content of appsettings.json which I then injected into another Service where I need content from the appsettings.json File. But somehow it's not possible to inject a Service into the DbContext class. What I mean with impossible is that I cannot create Migrations and apply them to the Database then
What I'm expecting is: I want to read the appsettings from my main ASP.NET Web Api project in the DbContext class of a C# Class library. Also it would be great to choose which appsettings.json file is used depending on the profile. F.e. appsettings.Development.json, appsettings.Stage.json etc.
You can run update command and pass it the startup project (your ASP.NET Web API one) via -s or --startup-project (check out common options in the CLI reference doc). So from the root of the solution it can look like:
dotnet ef database update -p /PathToDbContextProj -s /PathToWebAPIProj
Or from the data project folder:
dotnet ef database update ../PathToWebAPIProj
You can certainly inject an instance into your class that inherits from DbContext, e.g.
public class MyDatabaseDataContext : DbContext
{
private readonly IConfiguration _configuration;
public MyDatabaseDataContext(DbContextOptions<MyDatabase> dataContextOptions,
IConfiguration configuration) : base(
dataContextOptions)
{
_configuration = configuration;
}
// Your DbSet declarations....etc
public DbSet<Type> MySet { get; set; }
}
In your host process, you can initialise IConfiguration as normal, read from whichever sources you like, which might look something like
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration((context, config) =>
{
config.Sources.Clear();
var env = context.HostingEnvironment;
config
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("appsettings.local.json", optional: true, true)
.AddEnvironmentVariables();
});
webBuilder.UseStartup<Startup>();
});
}
So, configure the reading of appsettings/env variables in the top level host process, make those setting values available on IConfiguration (or, a strongly typed object if you should wish) then inject that into your DbContext instance
Related
In a .net core application, why I am always in "production", even if the ASPNETCORE_ENVIRONMENT is 'development'?
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder();
BuildConfig(builder);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Build())
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
// ...
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
switch (serviceToUse) { // ... } })
.UseSerilog()
.Build();
IService svc;
// ... got the corresp service...
svc.Run();
}
static void BuildConfig(IConfigurationBuilder builder) {
string env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? "Production";
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: true)
.AddUserSecrets<Program>()
.AddEnvironmentVariables();
}
}
However, when I test
I get rather unexpected values in the test setting:
[13:36:55 ERR] Environment.GetEnvironmentVariable('ASPNETCORE_ENVIRONMENT')=Development
[13:36:55 ERR] Environment.GetEnvironmentVariable('DOTNET_ENVIRONMENT')=Development
[13:36:55 ERR] The test setting is 'test value from secrets.json';
having this in projet:
My question is why does it take the Production settings if I am in Development mode?
It’s unclear to me where you are retrieving the configuration using this._config.GetValue<string>("test"). My guess is that you are retrieving it from the IConfiguration injected via DI.
In that case, you should note that the Host.CreateDefaultBuilder will also already come with its own configuration setup which does not really care about what you are doing before with your BuildConfig.
The default host builder will already set up configuration using the files appsettings.json and appsettings.<env>.json (and others) and will register that configuration as the IConfiguration singleton that is being provided to your application. In order to register the environment-specific file, it will use the environment from the IHostEnvironment which is what decides what actual environment your application is running on.
With ASP.NET Core and the web host, it is possible to configure this environment using the ASPNETCORE_ENVIRONMENT environment variable. However, since you are not using ASP.NET Core but just the host builder on its own, this environment variable is not being used. Instead, the default host builder will look for the environment variable DOTNET_ENVIRONMENT.
Alternatively, you can also call hostBuilder.UseEnvironment to set the environment explicitly if you want to take it from a different environment variable.
Finally, when you are setting up your own configuration, I would suggest you to reconfigure your host so that it will use this configuration object itself instead of building its own (which may or may not match your desired setup). You can do that by calling hostBuilder.ConfigureAppConfiguration.
I have 2 applications
App
ClassLibrary
App references ClassLibrary and both have their own appsettings.json file. My class library app has no startup.cs or program.cs so I have a static class that is supposed to read from the local appsettings.json file like this:
static class ConfigurationManager
{
public static IConfiguration AppSetting { get; }
static ConfigurationManager()
{
AppSetting = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
}
}
This is the appsettings.json in the class library
{
"AccountsAddress": "http://localhost:55260/api/Accounts/"
}
The problem starts when my app runs, it calls a method located in class library. However, the appsettings that is called is not the one in class library. Its the one in the app itself. Is there a way to specify that I want to reference the appsettings in the class library instead of the App.
Is there a way to specify that I want to reference the appsettings in the class library instead of the App.
Rename class library's appsettings.json file to something unique to that library.
something like {classlibname}.appsettings.json
After renaming it, make sure to set it to be copied to output directory.
Also make sure after renaming it that the code is calling the new name
//...
.AddJsonFile("classlibname.appsettings.json", optional: true, reloadOnChange: true)
//...
I am trying to provide some configuration data for my application so that we have simple settings stored in the source control, but our deployment system will replace the Encryption Key in appsettings.json when it deploys.
For development I still want a general key, but for specific users in their secrets I would like to provide a value that they can be sure will be secure.
I have my configuration files setup like this:
appsettings.json
{
"SystemConfiguration" : {
"EncryptionKey" : "weak"
}
}
appsettings.Development.json
{
"SystemConfiguration" : {
"EncryptionKey" : "devweak"
}
}
In my user secrets
{
"SystemConfiguration" : {
"EncryptionKey" : "this is a secret"
}
}
In my controller construction I'm getting the injected IConfiguration configuration.
And then
public SysConfigController(IConfiguration configuration)
{
var result = configuration["SystemConfiguration:EncryptionKey"];
}
but the value of result is always "weak" unless the higher level settings files don't contain the value at all (: null) doesn't work either.
Is there a way that I can retrieve the lowest level value?
Seems your configuration registrations are wrong. The last registration which contains the specific key will be used.
For example in your Program.cs file (assuming you are using ASP.NET Core, otherwise Startup.cs's constructor for ASP.NET Core 1.x) you can override the registrations (or just add the ones you like when you use the default host builder method):
public static IWebHostBuilder BuildWebHost(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddUserSecrets<Startup>()
.AddEnvironmentVariables()
.AddCommandLine(args);
})
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights();
In this example the following have been registered
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true):
Global configuration file
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
Enviornmentspecific configuration file, i.e. appsettings.Development.json
.AddUserSecrets<Startup>()
The user secrets
.AddEnvironmentVariables()
Environment variables
.AddCommandLine(args);
Command line parameters
When looking up for a key, the search happens in reverse order. If its defined as command line parameter, it will override all the other keys and values (from environment variable, user secrets, environment specific and global file).
So put the least important file at the beginning and most important (overriding) one at the end.
In this case if its defined in your user secrets, it will override the appsettings.Development.json and appsettings.json provided values.
From the docs
Configuration sources are read in the order that their configuration providers are specified at startup. The configuration providers described in this topic are described in alphabetical order, not in the order that your code may arrange them. Order configuration providers in your code to suit your priorities for the underlying configuration sources.
A typical sequence of configuration providers is:
Files (appsettings.json, appsettings..json, where is the app's current hosting environment)
User secrets (Secret Manager) (in the Development environment only)
Environment variables
Command-line arguments
It's a common practice to position the Command-line Configuration Provider last in a series of providers to allow command-line arguments to override configuration set by the other providers.
You can use method ConfigureServices(IServiceCollection services) in Startup.cs.
Describe your setting property in class and bind it. For example:
services.Configure<SystemConfiguration>(options => Configuration.GetSection("SystemConfiguration").Bind(options));
public class SystemConfiguration
{
public string EncryptionKey{get;set;}
}
Then, you can use DI for geting class
public class SomeClass
{
private readonly SystemConfiguration _systemConfiguration{get;set;}
public SomeClass (IOptions<ConfigExternalService> systemConfiguration)
{
_systemConfiguration = systemConfiguration;
}
}
I am trying to learn .Net core.
I have 2 to projects :
.Net Core Web api project
DLL .NetCore project contains my Unit of Work (entity framework)
My Questions:
How I can pass connection string from appsettings.json
to my DLL
I have in startup.cs configuration but I don t how to use it to access to my appsettings.json
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
there is services.AddDbContext in startup.cs
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")))
but I don't want to use services.AddDbContext because is a couple to entity framework and if I have to change to other ORM I have to change
this code also.
other question how have a responsibility to decrypt the connection
string.
a : webapi has to decrypt to the connection string and pass to DLL
(unit of work)
b: or unit of work has to decrypt the connection string
in the case I have another project (ie desktop application) how to
use DLL (unit of work) do I have to put also the connection
string inside my appsettings of the desktop application? (it like
kind of repetition ? )
I don't want to use services.AddDbContext because is a couple of entity framework
So what you need to do is create an extension method in your EF project that wraps this call so the dependency to EF stays within the EF project like the following:
public static IServiceCollection AddDatabase<TContext>( this IServiceCollection serviceCollection, Action<DbContextOptionsBuilder> optionsAction, string connectionString)
{
options.UseSqlServer(connectionString))
}
Then call this from your Startup Code:
services.AddDatabase(Configuration.GetConnectionString("DefaultConnection"));
That will resolve your dependency issue.
Regarding 2: see here: Encrypted Configuration in ASP.NET Core
Regarding 3: then the other projects will need to call the AddDatabase method and pass the connection string in, so the EF project will never have any knowledge about where to get it, it is always provided.
You can do it like this:
public BooksContext(string connectionString): this(GetOptions(connectionString))
{ }
private static DbContextOptions GetOptions(string connectionString)
{
return SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(), connectionString).Options;
}
You can get the in Stratup.cs file like this.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
private readonly IConfigurationRoot _appConfiguration;
private readonly IHostingEnvironment _hostingEnvironment;
public Startup(IHostingEnvironment env)
{
_hostingEnvironment = env;
_appConfiguration = env.GetAppConfiguration();
}
Code to get ConnectionString:
string connectionString = _appConfiguration["ConnectionStrings:Default"];
appsettings.json
{
"ConnectionStrings": {
"Default": "Server=YourDBAddress; Database=YourDB; Trusted_Connection=True;"
}
...
}
For other points, you can refer #Jamie Rees's answer.
In my Startup.cs class I have the following config build which initializes the db context:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json",true)
.AddEnvironmentVariables();
Configuration = builder.Build();
NHibernateUnitOfWork.Init(Configuration["ConnectionStrings:OracleConnection"]);
NHibernateUnitOfWork is located in a different project as a class library project. I want to be able to inject (or somehow pass the settings) instead of using the NHibernateUnitOfWork.Init in my Startup.cs class.
I can build the appsettings in my class library but trying to avoid that.
What is a sane way to do this ?
You can inject it.
In your Startup.cs class, you can add the following to your ConfigureServices(IServiceCollection services) method:
services.AddSingleton(Configuration);
From there, you can use constructor injection and consume it downstream the same way you normally would:
public class SomeClass
{
public SomeClass(IConfigurationRoot configuration)
{
NHibernateUnitOfWork.Init(Configuration["ConnectionStrings:OracleConnection"]);
}
}
NOTE: This assumes Configuration is defined in your Startup.cs class:
public static IConfigurationRoot Configuration;
See this:
Read appsettings json values in .NET Core Test Project
Essentially this is an integration solution using the TestServer. But if you're having to use the configuration, then it should probably be an integration test anyway.
The key to using the config in your main project inside your test project is the following:
"buildOptions": {
"copyToOutput": {
"include": [ "appsettings.Development.json" ]
}
}
which needs to go inside your main project's project.json