config.json - Adding database connection strings in ASP.NET vNext - c#

I am in the process of learning ASP.NET vNext. I need to store two connection strings in the config.json file. How do I store those? Can I do this:
config.json
{
"connectionStrings" : {
"connection-1" : "server=...",
"connection-2" : "server=..."
}
}
I have not been able to find a schema for config.json. For that reason, I wasn't sure how to do it. I saw the use of IConfiguration here. Still, I wasn't sure how Configuration was available in the app.

This is the official Configuration repo and this is a great blog post explaining the configuration system.
Long story short:
You need one or more Microsoft.Framework.ConfigurationModel.* NuGet packages in your application. The list of available packages is here.
Add the configuration sources that you need
Build the config file(s)
Read the configuration sources

Here is how you can do it:
{
"Data": {
"connection-1": {
"ConnectionString": "Server=..."
},
"connection-2": {
"ConnectionString": "Server=..."
}
}
}

This is how it's done these days...
{
"ConnectionStrings": {
"myDb1": "Server=myServer;Database=myDb1;Trusted_Connection=True;",
"myDb2": "Server=myServer;Database=myDb2;Trusted_Connection=True;"
}
}
From: Store and read connection string in appsettings.json

Related

Pass url parameter from Appsettings.json (.core) to React

I am trying to access the url parameter inside the appsettings.json file. The reason is that the api URL differs from Development to publish. I am not entirely sure the best way to solve this.
I've found a solution that could work:
how to get config data from appsettings.json with asp.net core & react
As I've understood from above thread, I need to create a service and call it from React? This seems abit wierd since I always(?) need to do a API request to the same project to recieve the URL which is needed for the API requests.
The other suggestion is to use webpack, or somehow save the url in the clientside. But won't this mean that whenever I need to change environment, I need to change that in 2 places (backend & frontend)?
This is how my appsettings.json file looks (same variable but different values for .Development and .Publish).
{
"Url": "localhost:44000"
}
In my Startup class I am getting the value:
var urlValue =
_configuration.GetSection("Url");
Can't I somehow get the value once depending on environment from the backend and recieve it using React?
Not sure if I am thinking wrong here?
Would appreciate if anyone is able to point me to the right direction.
My solution to this is using .env documents but still using .core launchSettings env.
For this solution to work, I was in need of env-cmd:
npm install env-cmd
Added 3 .env files:
1. .env
2. .env.development
3. .env.prod
Each file contains a url string:
REACT_APP_Url = https://localhost:44000
I added env check in .core startup file:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
else if (env.IsProduction())
{
spa.UseReactDevelopmentServer(npmScript: "build");
}
});
When I am running production code - I am refering to "build", so in my case the object "build" is going to run.
package.json file:
"scripts": {
"start": "rimraf ./build && react-scripts start",
"build": "cross-env env-cmd -f .env.prod react-scripts start",
}
I can now access the url by using:
{process.env.REACT_APP_Url}

Individual user settings in ASP.Net Core 3.1

I need to have bunch of settings for each user. Theese settings are same for all the users but every user can change their values. I'm looking for a good approach to create such an infrastructure. My best guess for now is table with all the settings in DB and a table with SettingId UserId and value. Not sure this is the best way tho.
In .net core , you can also use appsettings.json to store such configuration like as below:
{
"Users": {
"Arun": {
"Id": "ArunId",
"Name": "ArunName"
},
"Santa": {
"Id": "SantaId",
"Name": "SantaName"
}
}
}
you can directly convert these to strong type object with help of IConfiguration interface like below :
_configuration.GetSection("Users")

EF Core multiple database same schema

Using EF Core .net 2.2.
Trying to have an app where there is a "live" database and a "test" database backing my app. Currently I publish multiple sites each with their own DBContexts and just before publishing I comment out and swap the code for the connection string/db in my startup.cs.
ex:
//services.AddDbContext<DataContext>(options =>
// options.UseSqlServer(Configuration.GetConnectionString("TestDataContext")));
services.AddDbContext<DataContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LiveDataContext")));
Then my two sites are
testdata.site.com and livedata.site.com
This works but it is time consuming and inefficient whenever updates are made to the site/controllers/views etc. Plus if i ever wanted more than two sites to share the same database schema, the publishing work required would compound even more.
Here is my ideal solution but I don't know how to accomplish it:
I want to send route data to the controller and have the controller decide the connection string when it does this portion of the controller:
private readonly POSContext _context;
public CashierController(POSContext context)
{
_context = context;
}
Example, the URL would be something like:
www.site.com/{test or live}/{controller}/{action}
Then a user could swap between the databases on the fly if needed.
I can work through the routing portion but I am really stuck on what to with the controller and startup database portion to make this work.
Anyone have an idea or can get me going on the right path?
It all depends on how you publish your applications and what level of control you have on your hosting server.
You can use multiple configuration files which have different connection string values, so instead of having two connection string names, you should have only one, for example, "MyAppConnectionString", and use environment based configuration files to override it when needed.
To read more about configuration, visit:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2
Alternatively, you can use the hosting environment capability:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2.2
Please find some useful information in this answer as well:
Automatically set appsettings.json for dev and release environments in asp.net core?
This is what I ended up doing. I looked at what #Norcino said above and referenced the links in his post.
I created multiple Appsettings{DBIdentifier}.json files (still kept the usual appsettings.json file as well), ex appsettingsste3.json and in these JSON files I put a connection string, all with the same DB Name, but pointing to different DBs on my SQL server.
ex:
{
"ConnectionStrings": {
"SiteDBContext":\\Connection string for unique DB, all with same schema/tables, etc\\
}
}
In my program.cs I created a function that looks at the current directory on my web server, since each site is in its own folder on my webserver (ex d:\inetpub\wwwsites\ste1, d:\inetpub\wwwsites\ste3, d:\inetpub\wwwsites\ste3), then take the last four characters of that string then run a switch statement on adding the extra json file.
The portion of program.cs that i modified looks like this:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var dirStr = Directory.GetCurrentDirectory().ToString(); //Gets path of directory into string
var lastFour = dirStr.Substring(dirStr.Length - 4); //gets name of directory (last four characters in path)
switch (lastFour)
{
case "ste1":
case "ste2":
case "ste3":
string appStr = "appsettings" + lastFour.Substring(3) + ".json";
config.AddJsonFile(appStr, optional: false);
break;
}
})
.UseStartup<Startup>()
.Build();
Then of course, ConfigureServices in Startup.cs needs this:
services.AddDbContext<DataContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SiteDBContext")));
Have not yet performed hard testing to know what performance will be like but i think it should be fine since program.cs only runs when the app is first started and so once the app is running there shouldn't be any performance degradation at all. (Am I right?)

Missing ProviderName when debugging AzureFunction as well as deploying azure function

I have an issue getting a DbContext to correctly pull my connection string from my local.settings.json
Context:
This is an Azure function project
The main problem code is in System.Data.Entity.Internal.AppConfig
Although I have a local.settings.json file this is not dotnet core. It's .net 4.6.1
Error message:
'The connection string 'ShipBob_DevEntities' in the application's configuration file does not contain the required providerName attribute."'
Json configuration:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"ShipBob_DevEntities": {
"ConnectionString": "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string='data source=***;initial catalog=***;persist security info=True;User Id=***;Password=***;;multipleactiveresultsets=True;application name=EntityFramework'",
"providerName": "System.Data.EntityClient"
}
}
}
Configuration versions tested:
Moving the provider name into the actual ConnectionString token value : same error ocurrs
Setting the provider attribute inside the ConnectionString attribute to EntityClient: this did nothing
Making ShipBob_DevEntities a string value = to the value of ConnectionString : this throws new errors the likes of which are
keyword metadata is not supported
I tried using an ADO connection string which throws a code first exception which seems to occur when your connection string is incorrect in a database first approach.
I've taken the liberty to decompile EntityFramework.dll using dotPeek and have traced the problem down to System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig. Inside this method there is a call to LazyInternalConnection.FindConnectionInConfig which spits out a ConnectionStringSettings object that has it's ProviderName value set to null. Unfortunately I am unable to debug the AppConfig.cs class which it seems to use to generate this value so I am stuck.
So far I have consulted these two articles. One of which states to put the provider name as it's own token; however, this is not working.
https://github.com/Azure/azure-functions-cli/issues/193
https://github.com/Azure/azure-functions-cli/issues/46
Does anyone know the correct format to use in local.settings.json for an Entity Framework connection?
I went through several similar questions and answers here. Many of them are either misleading or assuming everybody is on the same level and understands how the azure functions are working. there is no answer for newbies like me. I would like to summarize here my solution step by step. I dont think that provided answer is the best option because it forces you to change the auto generated edmx files which can be overwritten by mistake or next update of your edmx from database. Also best option here is to use Connection strings instead of App settings in my opinion.
most important thing is that we understand local.settings.json file
IS NOT FOR AZURE. it is to run your app in the local as the name is
clearly saying. So solution is nothing to do with this file.
App.Config or Web.Config doesnt work for Azure function connection strings. If you have Database Layer Library you cant overwrite connection string using any of these as you would do in Asp.Net applications.
In order to work with, you need to define your connection string on the azure portal under the Application Settings in your Azure function. There is
Connection strings. there you should copy your connection string of your DBContext. if it is edmx, it will look like as below. There is Connection type, I use it SQlAzure but I tested with Custom(somebody claimed only works with custom) works with both.
metadata=res:///Models.myDB.csdl|res:///Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System.Data.SqlClient;provider
connection string='data source=[yourdbURL];initial
catalog=myDB;persist security info=True;user
id=xxxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework
After you set this up, You need to read the url in your application and provide the DBContext. DbContext implements a constructor with connection string parameter. By default constructor is without any parameter but you can extend this. if you are using POCO class, you can amend DbContext class simply. If you use Database generated Edmx classes like me, you dont want to touch the auto generated edmx class instead of you want to create partial class in the same namespace and extend this class as below.
This is auto generated DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
this is the new partial class, you create
namespace myApp.Data.Models
{
[DbConfigurationType(typeof(myDBContextConfig))]
partial class myDBEntities
{
public myDBEntities(string connectionString) : base(connectionString)
{
}
}
public class myDBContextConfig : DbConfiguration
{
public myDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
}
After all you can get the connection string from azure settings, in your Azure Function project with the code below and provide to your DbContext
myDBEntities is the name you gave in the azure portal for your connection string.
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;
using (var dbContext = new myDBEntities(connString))
{
//TODO:
}
So the solution ended up being trivial. The ProviderName attribute specified in local.settings.json MUST be camel case.
From the original git hub discussions :
https://github.com/Azure/azure-functions-cli/issues/46
Shows the provider name as being pascal case
https://github.com/Azure/azure-functions-cli/issues/193
Shows the provider name being camel case in pseudo code
It was very easy to miss but your config section must be exactly as follows
"ConnectionStrings": {
"ShipBob_DevEntities": {
"ConnectionString": "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string='data source=***;initial catalog=***;persist security info=True;User Id=***;Password=***;;multipleactiveresultsets=True;application name=EntityFramework'",
"ProviderName": "System.Data.EntityClient"
}
}
These points are important:
Make sure your connection string has metadata information
If copying your string from an xml config, make sure you unescape apostrophes
Make sure the ProviderName attribute is camel case
Make sure the provider name is System.Data.EntityClient
Fix for missing providername in deployment
Note, this answer assumes you are trying to use the parameterless constructor of a DbContext. If you are creating new code you can easily follow the second upvoted answer
I figured out a way to circumvent the provider name issue while still retaining the use of the portal config and thus deployment slots. It involves setting the default connection string of db context using static properties
private static string _connectionString = "name=ShipBob_DevEntities";
static ShipBob_DevEntities()
{
if(!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AzureFunction")))
{
var connectionString = System.Environment.GetEnvironmentVariable("EntityFrameworkConnectionString");
if (!string.IsNullOrEmpty(connectionString))
{
_connectionString = connectionString;
}
}
}
public ShipBob_DevEntities()
: base(_connectionString)
{
this.Configuration.LazyLoadingEnabled = false;
}
This involves the developer to create an app setting in the azure portal as a flag. In my case it is AzureFunction. This makes sure that our code is only run in an azure function and all other clients of this DbContext, whether they be web apps, windows apps, etc, can still continue behaving as expected. This also involves adding your connection string to the azure portal as an AppSetting and not an actual connection string. Please use the full connection string including them metadata information but without the provider name!
EDIT
You will need to edit your auto generated .tt file t4 template to make sure this code does not get overridden if you are using db first.
Here is a link on the T4 syntax: https://learn.microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template
And here is an explanation on EF T4 templates: https://msdn.microsoft.com/en-us/library/jj613116(v=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe
I encountered the similar issue before, I would use the following approach for achieving my purpose, you could refer to it:
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<AccountKey>",
"AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<AccountKey>",
"sqldb-connectionstring": "Data Source=.\\sqlexpress;Initial Catalog=DefaultConnection;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
},
"ConnectionStrings": {
"Bruce_SQLConnectionString": "Data Source=.\\sqlexpress;Initial Catalog=DefaultConnection;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
}
}
For retrieving the connection string:
var connString = ConfigurationManager.AppSettings["sqldb-connectionstring"];
//or var connString = ConfigurationManager.ConnectionStrings["Bruce_SQLConnectionString"].ConnectionString;
using (var dbContext = new BruceDbContext(connString))
{
//TODO:
}
Or you could init your no-argument constructor for your DbContext as follows:
public class BruceDbContext:DbContext
{
public BruceDbContext()
: base("Bruce_SQLConnectionString")
{
}
public BruceDbContext(string connectionString) : base(connectionString)
{
}
}
Then, you could create the instance for your DbContext as follows:
using (var dbContext = new BruceDbContext(connString))
{
//TODO:
}
Moreover, you could refer to Local settings file for Azure Functions.
Here are two approaches that work for me:
Approach 1
Add the connection string to the App Settings (respectively local.settings.json) in the following format:
metadata=res:///xxx.csdl|res:///xxx.ssdl|res://*/xxx.msl;provider=System.Data.SqlClient;provider connection string='data source=xxx.database.windows.net;initial catalog=xxx;user id=xxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework'`
Go to the class that extends DbContext ("TestEntities") and extend the constructor to take the connection string as argument
public partial class TestEntities: DbContext
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
If you want then to interact with the database you need to retrieve the connection string from the app settings and then pass it over when initializing DbContext
string connectionString = Environment.GetEnvironmentVariable("connectionStringAppSettings");
using (var dbContext = new TestEntities(connectionString))
{
// Do Something
}
The problem with this approach is that every time you update the database you need to update the class "TestEntities" as it is overwritten
Approach 2
The goal here is to leave the class "TestEntities" as is to avoid the issue from Approach 1
Add the connection string to the App Settings (respectively local.settings.json) like in Approach 1
Leave TestEntities as is
public partial class TestEntities : DbContext
{
public TestEntities ()
: base("name=TestEntities")
{
}
As TestEntities is partial you can extend that class by creating another one that is also partial with the same name in the same namespace. The goal of this class is to provide the constructor that takes the connection string as argument
public partial class TestEntities
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
}
Then you can go on like with Approach 1

Configuration manager can't get connection string in ASP.net 5

I have an ASP.NET 5 web application that has pulled in a .NET 4.6 class library. At some point in the class library there is a call to get a connection string from web.config:
ConfigurationManager.ConnectionStrings["AppDataConnectionString"].ConnectionString
(NOTE: I CAN NOT CHANGE THIS CODE.)
This class library has been used in old web forms applications, where the AppDataConnectionString was defined in their web.configs. Now I'm trying to use the class library in my ASP.NET 5 web app, but the above code throws a null reference exception.
Here is the connection strings section in my web.config in the ASP.NET 5 project:
<connectionStrings>
<add name="AppDataConnectionString" connectionString="server=xxxxxx;database=yyyyy;Trusted_Connection=yes;" providerName="System.Data.SqlClient" />
</connectionStrings>
I've also tried adding it in an appsettings.json file as follows:
{
"ConnectionStrings": {
"AppDataConnectionString": {
"ConnectionString": "server=xxxxx;database=yyyyy;Trusted_Connection=yes;"
}
}
}
Here is where I load the configuration in Startup:
public IConfiguration _Configuration { get; set; }
public Startup()
{
_Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(provider => _Configuration);
}
public void Configure(IApplicationBuilder app)
{
// Call some code from the class library that tries to get that connection string.
}
It seems to me like the configuration manager just can't find where the connection string is. Do I have my web.config or appsettings.json structured in the right way for it to find it?
It turns out that any configuration that was stored previously in a web.config file in previous versions of ASP.net now needs to be stored in an app.config file in the same directory as project.json in ASP.net 5.0. Now my imported class libraries can get their connections strings and everything works.

Categories

Resources