Net 6 Multiple Connection Strings - c#

How to setup in Net 6 program.cs a multiple connection strings?
I want to work with Development, Staging, and Production environments, all of them pointing to different database servers.
NET 6. Program.cs:
builder.Services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
Thanks in advance.

Here's what you could do.
First, create an appsettings.json like this:
appsettings.json
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
},
"WillAppConfig": {
"ActiveEnvironment": "Development",
"DevDatabase": "server:123.123.123.123, user: will, pass:1234",
"STGDatabase": "server:123.123.123.123, user: will, pass:1234",
"ProdDatabase": "server:123.123.123.123, user: will, pass:1234"
}
}
Then create a class somewhere in your project, that will serve to map the configuration to an object.
WillAppConfigurationMap.cs
public class WillAppConfigurationMap
{
public string ActiveEnvironment { get; set; }
public string DevDatabase { get; set; }
public string STGDatabase { get; set; }
public string ProdDatabase { get; set; }
}
Finally in your Program.cs, you could select the connection string to use depending on the value of ActiveEnvironment.
var builder = WebApplication.CreateBuilder(args);
WillAppConfig = builder.Configuration.GetSection("WillAppConfig").Get<WillAppConfigurationMap>();
var connectionString = "";
if (WillAppConfig.ActiveEnvironment == "Development")
{
connectionString = WillAppConfig.DevDatabase
}
else if (WillAppConfig.ActiveEnvironment == "Staging")
{
connectionString = WillAppConfig.STGDatabase
}
else if (WillAppConfig.ActiveEnvironment == "Production")
{
connectionString = WillAppConfig.ProdDatabase
}
builder.Services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(connectionString));
});
partial class Program
{
public static WillAppConfigurationMap WillAppConfig { get; private set;}
}
You can remove the "Kestrel" section from the appsettings.json if you don't use it. You can use this approach to map any appsettings.json structure.
Then, you can access your configuration object from ANYWHERE in your app doing Program.WillAppConfig.

Create multiple connection strings with different names in appsetting.json:
builder.Services.AddDbContext<MyContext>(options => {
options.UseSqlServer(builder.Configuration.GetConnectionString(UseNameofConnectionString));
});
Also you can create extension method which gives you required connectionString when you call it.

Related

.NET Core pass configuration section via command line

In appsettingsjson file i have:
"DataSource": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "Root",
"CollectionName": "ApiLog"
},
in Program.cs, i get this data like this
builder.Services.Configure<DatabaseSettings>(
builder.Configuration.GetSection("DataSource"));
where DatabaseSettings class is;
public class DatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
}
Then i can access instance of DatabaseSettings via dependency injection like:
public class LogService
{
private readonly IMongoCollection<Log> _collection;
public LogService(
IOptions<DatabaseSettings> databaseSettings)
{
var mongoClient = new MongoClient(
databaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
databaseSettings.Value.DatabaseName);
_collection = mongoDatabase.GetCollection<ElekseLog>(
databaseSettings.Value.CollectionName);
}
}
the question is i dont want to store db info in appsettings json file. I want to pass tis info from command line without changing the code. How can I achieve this?
You need to "flatten" the parameters by joining "path elements" with : and pass as key-value pairs. For example:
yourapp "DataSource:ConnectionString"="mongodb://localhost:27017"
Or
yourapp --DataSource:ConnectionString=mongodb://localhost:27017
Some info can be found in the docs - Command-line configuration provider.

Why am i getting argumentNullException when adding migration

I'm trying to create a migration in my web api for one-to-many db. Services are configured this way and the connection string is successfully received from launchsettings.json
var connectionStr = Environment.GetEnvironmentVariable("ApplicationDbContext");
services.AddDbContext<InsolationResultContext>(options =>
{
options.UseNpgsql(connectionStr, builder =>
{
builder.CommandTimeout(300);
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
});
});
This are models
public class DocumentInsolationResult
{
public string Id { get; set; }
public string UserId { get; set; }
public IEnumerable<InsolationResult> Elements { get; set; }
}
public class InsolationResult
{
public string UniqueId { get; set; }
public string Insolation { get; set; }
}
And DbContext
public class InsolationResultContext : DbContext
{
public DbSet<DocumentInsolationResult> DocumentInsolationResults { get; set; }
public DbSet<InsolationResult> InsolationResults { get; set; }
public InsolationResultContext(DbContextOptions<InsolationResultContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DocumentInsolationResult>()
.HasMany(p => p.Elements)
.WithOne();
}
}
the connection string looks like this:
"environmentVariables": {
"ApplicationDbContext": "Host=192.168.1.***;Port=****;Database=***.******;Username=*****;Password=****",
"ASPNETCORE_ENVIRONMENT": "Development"
},
When trying to "Add-Migartion Init i always get "Value cannot be null. (Paramtere 'connectionString'). What am i doing wrong? Coulnd't actually find the answer on the internet
upd: i'me receiving the connection string from launchsetting, it's ok as i'm using the same way of getting connectionString on some other projects
upd2 hardcoding the connection string worked for me
If you use IConfiguration instead of Environment.GetEnvironmentVariable then it becomes a lot easier:
var connString = config.GetValue<string>("ApplicationDbContext");
It should be
"ConnectionStrings": {
"ApplicationDbContext": "Host=192.168.1.***;Port=****;Database=***.******;Username=*****;Password=****""
}
in appsettings.Development.json file.
Thus it is giving error "Value cannot be null. (Paramtere 'connectionString')."
And in your startup.cs file
services.AddDbContext<InsolationResultContext >(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext")
});
Well the problem was really with the connection string. I couldn't create a migration when using connection string from appsettings or launchsettings. After hardcoding the connection string in stratup.cs i was able to create migration. And now after the db is initialized i'm using connection string from launchsettings. This might be some EF core tricky things.

How to read values from multiple json files in .Net Core?

I want to read values from multiple json files. Getting the values from default json file but unable to read separately added json file. below is my code.
//Reads from endpoint-defaults.json
var endpointDefaultsBuilder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("endpoint-defaults.json", true, true)
.AddEnvironmentVariables();
ConfigurationEndpointDefaults = endpointDefaultsBuilder.Build();
string signOnType = ConfigurationEndpointDefaults.GetSection("AppSettings:mastercard.mip.signOnType").Value;
var mscdGamingList = ConfigurationEndpointDefaults.GetSection("AppSettings:paymentPostCountryProhibitionCheckStage.mscdGamingList.list").Get<string[]>();
//Reads from config.json
var configbuilder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("jsconfig1.json", true, true)
.AddEnvironmentVariables();
Configuration = configbuilder.Build();
string environmentMode = Configuration.GetSection("AppSettings:mastercard.mip.hostname").Value;
Here is the output.
My Json Files
endpoint-defaults.json
{
"AppSettings": {
//Default MasterCard properties
"mastercard.mip.signOnType": "ONDEMAND",
"mastercard.mip.responseTimeout": 30,
}
}
jsconfig1.js
{
"AppSettings": {
"mastercard.mip.hostname": "localhost",
"mastercard.mip.authorisation_port": 19092,
"mastercard.mip.test_authorisation_port": 19094,
}
}
You can always use multiple json configuration files in building configuration of a .net core application. You can also include XML configuration file.
I have very little to no knowledge about the the configuration structure and the values you are using. So here I will explain with a simple configuration examples and a asp.net core application.
I have two configuration files.
appsettings.json
apisettings.json
Both the above configuration files have multiple configuration sections in them.
//appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"DbSettings": {
"ConnectionString": "Data Source=localhost; Initial Catalog=CareCMSDatabase;User ID=sa; Password=Password1; TimeOut=30;"
},
"SmtpSettings": {
"UserName": "someemail#domain.com",
"Host": "someserver.smtp.com",
"Port": "587",
"Password": "password"
}
}
//apisettings.json
{
"ProductApi": {
"BaseUrl": "https://www.products.com/",
"AuthEndpoint": "api/2.0/Auth/",
"ClientId": "somelcientid",
"ClientSecret": "someclientsecret"
},
"SearchApi": {
"BaseUrl": "https://www.search.com/",
"RandomEndpoint": "api/random",
"AuthToken": "sometoken"
}
}
Then I create C# classes to represent the configuration sections.
public class SmtpSettings
{
public string Host { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int Port { get; set; }
}
public class DbSettings
{
public string ConnectionString { get; set; }
}
public class ProductApiSettings
{
public string BaseUrl { get; set; }
public string AuthEndpoint { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class SearchApiSettings
{
public string BaseUrl { get; set; }
public string RandomEndpoint { get; set; }
public string AuthToken { get; set; }
}
Now I add these JSON files to the configuration builder while building Creating HostBuilder in Program.cs file.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration((builder) => {
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile("apisettings.json")
.AddEnvironmentVariables();
});
webBuilder.UseStartup<Startup>();
});
The above code will add the json configuration files to the configuration builder and build the configuration.
I can now retrieve the configuration sections and translate them to C# classes and use the configuration values. In following code I am accessing the configuration sections and their values inside ConfigureServices method of Startup class.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Get DbSettings section from the configuration file.
var dbSettingSection = Configuration.GetSection("DbSettings");
// Get Configuration value and convert it to C# class object.
var dbSettings = dbSettingSection.Get<DbSettings>();
// Now I can access ConnectionString value from the configuration by accessing dbSettings.ConnectionString
//Same as above, get ProductApi Section from the configuration file.
var productApiSection = Configuration.GetSection("ProductApi");
// Get the configuartion value and convert it to C# class object.
var productApiSettings = productApiSection.Get<ProductApiSettings>();
var smtpSection = Configuration.GetSection("SmtpSettings");
var smtpSettings = smtpSection.Get<SmtpSettings>();
var searchApiSection = Configuration.GetSection("SearchApi");
var searchApiSettings = searchApiSection.Get<SearchApiSettings>();
var authToken = Configuration["SearchApi:AuthToken"];
services.AddControllersWithViews();
}
You can also inject these configuration sections as dependencies in other parts of the application such as controller or service class.
For that, you need to add the configuration sections to the service collections so that they get resolved as dependencies. Change ConfigureService method as following.
public void ConfigureServices(IServiceCollection services)
{
var dbSettingSection = Configuration.GetSection("DbSettings");
// Add the section to service collection.
services.Configure<DbSettings>(dbSettingSection);
var productApiSection = Configuration.GetSection("ProductApi");
services.Configure<ProductApiSettings>(productApiSection);
var smtpSection = Configuration.GetSection("SmtpSettings");
services.Configure<SmtpSettings>(smtpSection);
var searchApiSection = Configuration.GetSection("SearchApi");
services.Configure<SearchApiSettings>(searchApiSection);
services.AddControllersWithViews();
}
Now I can have dependency on, let say ProductApi config section, in my HomeController as following.
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
// Field for ProductApiSettings
private ProductApiSettings _productApiSettings;
public HomeController(IOptions<ProductApiSettings> productsApiSettings, Logger<HomeController> logger)
{
_logger = logger;
// Initilizing settings object from the dependency.
// This will have the values retrieved from the json config files.
_productApiSettings = productsApiSettings.Value;
}
public IActionResult Index()
{
// Using properties from the settings
var productApiAuthURL = _productApiSettings.BaseUrl + _productApiSettings.AuthEndpoint;
return View();
}
}
I hope this will help you solve your issue.

How to default provider from appsettings.json using if else?

I have 2 providers as you can see below. If "IsDefaultProvider" section is "true" I want to get its values while injecting IConfiguration.
I write the below code I could not work
var providers = configuration.GetSection("X:Providers");
foreach(var provider in providers.)
{
if (providers.GetSection("IsDefaultProvider").Value == "true")
{
_defaultProvider = (XProviderType)Enum.Parse(typeof(XProviderType), provider.Value.ToString());
}
}
Appsettings.json
"X": {
"Providers": [
{
"IsDefaultProvider": false,
"Name": "***",
"BaseUrl": "https://*",
"ApiKey": "*****",
"SecretKey": "****"
},
{
"IsDefaultProvider": true,
"Name": "*****",
"BaseUrl": "http://*"
}
]
}
There are two problems in the code you've shown in your question:
providers. does not compile. I expect this might just be a bad copy-paste but I'm pointing it out just in case as it should be providers.GetChildren().
When parsing a JSON boolean value, the string representation is e.g True rather than true, so you need to compare against this (or just do so case-insensitively).
Here's a working example:
var providers = configuration.GetSection("X:Providers");
foreach (var provider in providers.GetChildren())
{
if (provider.GetSection("IsDefaultProvider").Value.ToLower() == "true")
{
_defaultProvider = ...
}
}
I would also swap out the use of GetSection("IsDefaultProvider"), simply because IsDefaultProvider is not a section: it's a property. Here's an example of how that simplifies the code a little:
if (provider["IsDefaultProvider"].ToLower() == "true")
Having said all of that, Tao Zhou's answer is a much more type-safe way to handle this process if you have no objections to creating those additional classes, so I'd encourage you to consider using his approach instead.
For mapping Configuration from appsettings.json to Provider, try follow steps below:
Define model for Provider
public class Provider
{
public bool IsDefaultProvider { get; set; }
public string Name { get; set; }
public string BaseUrl { get; set; }
public string ApiKey { get; set; }
public string SecretKey { get; set; }
}
public class X
{
public List<Provider> Providers { get; set; }
}
Get Default Provider
var providers = Configuration.GetSection("X").Get<X>();
foreach (var provider in providers.Providers)
{
if (provider.IsDefaultProvider == true)
{
//var _defaultProvider = (XProviderType)Enum.Parse(typeof(XProviderType), provider.Value.ToString());
}
}

How to handle a hierarchy of properties in ASP.NET 5 AppSettings?

In ASP.NET 4 to organize settings, I am prefixing the setting key with a small word that indicates where this config is used (e.g. key="dms:url", "sms:fromNumber" .... etc).
In ASP.NET 5, the AppSettings configuration is mapped to a strongly typed class.
what is the property that i need to build for "dms:url"? How could map dashes & special chars to a C# property in ASP.NET 5?
You can organize your configuration file within a hierarchy in the config.json
{
"AppSettings": {
"SiteTitle": "PresentationDemo.Web",
"Dms": {
"Url": "http://google.com",
"MaxRetries": "5"
},
"Sms": {
"FromNumber": "5551234567",
"APIKey": "fhjkhededeudoiewueoi"
}
},
"Data": {
"DefaultConnection": {
"ConnectionString": "MyConnectionStringHere. Included to show you can use the same config file to process both strongly typed and directly referenced values"
}
}
}
We defined the AppSettings as a POCO class.
public class AppSettings
{
public AppSettings()
{
Dms = new Dms(); // need to instantiate (Configuration only sets properties not create the object)
Sms = new Sms(); // same
}
public string SiteTitle { get; set; }
public Dms Dms { get; set; }
public Sms Sms { get; set; }
}
public class Dms
{
public string Url { get; set; }
public int MaxRetries { get; set; }
}
public class Sms
{
public string FromNumber { get; set; }
public string ApiKey { get; set; }
}
We then load the configuration into an instance of IConfigurationSourceRoot and then set values of AppSettings using GetSubKey. The best practice would be to do this in ConfigureServices and add it to the DI Container.
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
}
public void ConfigureServices(IServiceCollection services)
{
// Add Application settings to the services container.
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
//Notice we can also reference elements directly from Configuration using : notation
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
}
We can now provide access in a controller through the constructor. I set the setting values explicitly the constructor but you could use the entire IOptions
public class HomeController : Controller
{
private string _title;
private string _fromNumber;
private int _maxRetries;
public HomeController(IOptions<AppSettings> settings)
{
_title = settings.Options.SiteTitle;
_fromNumber = settings.Options.Sms.FromNumber;
_maxRetries = settings.Options.Dms.MaxRetries;
}
If you wanted to keep everything flat and use a pseudo hierarchy like you have been doing, you can, but ":" isn't a valid symbol for a variable name. You would need to use a valid symbol like "_" or "-" instead.

Categories

Resources