Updating databases in Entity Framework for development and production environments in ASP.NET Core - azure

How do I apply EF migrations to multiple databases, referencing either a Development or Production environment as required?
TL;DR
I'm developing an ASP.NET Core MVC application using Entity Framework that references different databases for Development and Production. Both databases are hosted Azure SQL databases.
The application at the moment is running on IIS Express on my Development machine, but I will deploy Production to an Azure App Service. I'm using Entity Framework and migrations to update the database design.
I've applied this answer to use a different database connection string for either the Development or Production launch configurations, and this seems to be working. The connection strings are then stored in appsettings.Development.json or appsettings.Production.json.
When I select the DEV launch configuration, I am able to use the EF cli command dotnet ef database update in order to apply my migrations to the development database. Works fine.
However, I cannot work out how to tell Visual Studio to apply each migration to the production database when I run under the PROD launch configuration.
Running the command dotnet ef database update after changing the launch configuration continues referencing the development database, and nothing changes. I'm not surprised by this - I haven't told Visual Studio where to find the other database. But I also can't work out how to do this.
Is there a way to change the referenced database, based on the launch configuration or environment variables? Or some other convenient way?
launchSettings.json
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:57961",
"sslPort": 44320
}
},
"profiles": {
"IIS Express (dev)": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express (prod)": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production"
}
}
}
}

According to the documentation, you can specify the connection string as an extra parameter:
--connection: The connection string to the database. Defaults to the one specified in AddDbContext or OnConfiguring.
So this command will let you specify which database you are targeting:
dotnet ef database update --connection "<sql connection string>"

Related

Use different connectionString in production environment

i am new to Web-Development and so it is the first time, that I try to work with different environments.
So in my WebApp which is deployed on Azure I want to use a connectionString for my local database, when I am working locally in development environment, and when I deploy it, it should use a different database with another connectionString.
I have seen that there are two files in my Asp.Net Core project. "appsettings.json" and "appsettings.Development.json". If I understand correctly, app.dev.json should override the settings in app.json if I work in Development Environment. But it doesn`t. When I am debugging the app, to realy make sure that environment is set to development, it still uses appsettings.json.
You might be correct in term of Multiple Configuration Files. appsettings.json is a default configuration file that you can declare anything which includes both Development and Production. The merging pattern will be appsettings.{Environment}.json, then all matching keys will replace all keys in appsettings.json. Remembered the name Environment should be matched with ASPNETCORE_ENVIRONMENT to be used. In you case, app.dev.json so your environment should be dev (case-sensitive) instead of Development.
For example: We have a default `appsettings.json
{
"ConfigurationString": "mongodb://localhost:27017",
"MongoOptions": {
"AllowWireUp": true,
"AutoConnect": true
}
}
Then you want to work in Development, you create appsettings.Development.json with content
{
"ConfigurationString": "mongodb://192.168.1.1:27017",
"MongoOptions": {
"AllowWireUp": false
}
}
Later when you run with Development, you will get combined file
{
"ConfigurationString": "mongodb://192.168.1.1:27017",
"MongoOptions": {
"AllowWireUp": false
}
}
Important: You will see MongoOptions.AutoConnect is false in Development because .NET Core merges two files based on first level key instead of merging nested. That means MongoOptions in appsettings.Development.json will replace entire your appsettings.json
There is a way to do that. I guess you are using Azure app service?. if so follow thease steps
Create multiple app-settings files inside your project. (dev/ staging / uat / prod)
These file names shoud be appsettings.Development.json appsettings.uat.json and appsettings.Production.json
Each file should contain its own configurations.
Then Go to your App service in azure > configuration > Application settings , add required prifix of your appsettings json file in Value filed in ASPNETCORE_ENVIRONMENT
Restart app service. it should work now

Events not flowing to Application Insights when using secrets.json in localhost

I used this Application Insights getting started guide for setting up my application. I'm running VS2017 version 15.5.7 and my application is using AspNetCore 2.0.0.
When I F5 debug my application using IIS Express from Visual Studio, I see Application Insights events within the debugger Events window. However, the same events are not flowing to Application Insights in Azure; I've configured the InstrumentationKey within secrets.json as you can see below. I've confirmed the key is loaded into my application configuration by setting a breakpoint.
As another debugging data point, I've confirmed events do successfully flow to Application Insights when running in a Azure Web App. This test uses the exact same code. Instead of loading the InstrumentationKey from secrets.json, however, I've configured APPINSIGHTS_INSTRUMENTATIONKEY environment variable to have the key (via ApplicationSettings pane of the Web App in the portal).
I'm at a loss for why my events are not flowing to Application Insights via localhost debugger, but they are when deployed to a Web App.
Program.cs
public static void Main( string[] args )
{
BuildWebHost( args ).Run();
}
public static IWebHost BuildWebHost( string[] args ) =>
WebHost.CreateDefaultBuilder( args )
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
Startup.cs
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath( hostingEnvironment.ContentRootPath )
.AddJsonFile( $"appsettings.{hostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true );
if ( hostingEnvironment.IsDevelopment() )
{
configurationBuilder.AddUserSecrets<Startup>( optional: true );
}
this.Configuration = configurationBuilder
.AddEnvironmentVariables()
.Build();
secrets.json
{
"ApplicationInsights": {
"InstrumentationKey": "omitted for StackOverflow"
}
}
my.csproj
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.1.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Web" Version="2.5.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
launchSettings.json
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:2322/",
"sslPort": 44330
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "https://localhost:44330/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"FrontDoor": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "https://localhost:44330/api/config",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:2323"
}
}
}
when you see appinsights events in the debugger output window, do those events say (unconfigured) in them? if that is present in the output window, that would mean that the sdk is running in debug, but not finding the instrumentation key in your appsettings.json (or in code) note that the application insights sdks don't look in usersecrets for this setting, only appsettings! (edit 5/28/2020: see OP's edit below, while this was the issue 2 years ago, if you use AddApplicationInsightsTelemetry instead of the obsolete UseApplicationInsights this limitation does not apply)
if the events in the debugger window do not say (unconfigured), the next step is to use something like Fiddler to watch your outbound http traffic, to make sure you're seeing outbound calls by the SDK going to dc.services.visualstudio.com, and that those outbound calls are succeeding. (and at this step, you can verify that the instrumentation key think you are using are using is the one the sdk is using to send the events)
if the events are being sent, and are using the ikey you have set, the last step is to verify that the ikey you're using is the one for the resource you're looking at in the portal. every once in a while, someone has an ikey for "dev" somewhere, and then is looking at a "prod" resource in the portal and not seeing the events they expect.
if you've gotten this far, with the events being sent, sent to the ikey you want, and verified the ikey is for the resource you expect it to be, then verify that there aren't any service outages or delays that might be affecting your data, which you can find at http://aka.ms/aistatus otherwise, you should see events normally in seconds~minutes depending on where in the world you are and what region your resource is in azure, etc.
OP edit for completeness of answer:
Indeed, I was hitting #1 and as John hinted in the comments, I needed to let AI SDK know about my iKey in another way. Instead of using UseApplicationInsights() in Program.cs, I've switched to using AddApplicationInsightsTelemetry(configuration) within StartUp.ConfigureServices(). The configuration passed along has my iKey loaded from secrets.json.
This is a known limitation, being fixed in the coming beta.
https://github.com/microsoft/ApplicationInsights-dotnet/issues/1882
Until then workaround is to use the AddApplicationInsights(IConfiguration) overlaod.
Starting from Microsoft.ApplicationInsights.AspNetCore version 2.15.0 this problem has been fixed.
For lower versions the overload services.AddApplicationInsightsTelemetry(Configuration) would read also from secrets.json.
User secrets and other configuration providers
If you want to store the instrumentation key in ASP.NET Core user
secrets or retrieve it from another configuration provider, you can
use the overload with a
Microsoft.Extensions.Configuration.IConfiguration parameter. For
example, services.AddApplicationInsightsTelemetry(Configuration);.
Starting from Microsoft.ApplicationInsights.AspNetCore version 2.15.0,
calling services.AddApplicationInsightsTelemetry() will automatically
read the instrumentation key from
Microsoft.Extensions.Configuration.IConfiguration of the application.
There is no need to explicitly provide the IConfiguration.
https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core

Deploy .netcore to iis

I have a .net core app and runs fine on kestrel server.
http://localhost:5000/mapapi/v1.0/branch
I have deployed the app to IIS using publish tool in visual studio 2017 and with in program.cs, I have this line of code
.UseIISIntegration()
when I run the app I get an error
An error occurred while starting the application.
Exception: Cannot read variable
It is not reading the variable from launchsettings.json. I also created a profile by the name "IIS"
"IIS": {
"commandName": "iis",
"launchBrowser": true,
"launchUrl": "mapapi/v1.0/Branch",
"environmentVariables": {
"ConnectionString": "myhiddenconnectionstring",
}
}
What am I doing wrong? what other places do I have to look and make sure the application reads the values appropriately. Anyone in the community who had experience .net core to IIS, please share you knowledge with the community. We would appreciate it.

How to Connect to Azure database using the standard dotnet web template

I am super excited to start developing .NET on my Mac but getting frustrated trying to figure it all out. So I have a WebApplication created by calling yo aspnet and out of the box it works great. It comes with a connection to a local db stored in the project. Where I am having trouble is connecting it to a remote DB hosted in my azure portal.
My Connection string below directly from my azure portal. I have updated my appsettings.json "DefaultConnection" property to this value (with the username and password missing below) and I am getting errors like "ArgumentException: Keyword not supported: 'server'." I have tried several different connection strings and none are working.
Admittedly I am new to all of this so I am probably missing something simple but in searching online I have yet to find the solution.
Server=tcp:mydbserver.database.windows.net,1433;Initial Catalog=LH;Persist Security Info=False;User ID={your_username};Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
For reference here is my whole appsettings.json (without username & passwrod...)
{
"ConnectionStrings": {
//"DefaultConnection": "Data Source=LH.db"
"DefaultConnection": "Server=tcp:mydbserver.database.windows.net,1433;Initial Catalog=lighthouseFSQ;Persist Security Info=False;User ID={Username};Password={Password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
Any help is greatly appreciated. FYI I did try this in Windows VS2015 and it worked fine.
By default, the dotnet new -t web tempalte uses SQLite database, so only changing the connection string is not sufficient to change the provider.
You also need to remove the Microsoft.EntityFrameworkCore.Sqlite package from project.json and replace it with Microsoft.EntityFramework.SqlServer (assuming you are using the SQLServer of Azure). Then in your Startup.cs you need to replace the
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
with
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
and change the namespaces if necessary.
Update
In project.json you just need to change the dependency with Sqlite.
"dependencies": {
...
"Microsoft.EntityFrameworkCore.Sqlite": "1.0.1",
...
}
}
to
"dependencies": {
...
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.1",
...
}
}
If there is also a Microsoft.EntityFrameworkCore.Sqlite.Design ch ange that too to Microsoft.EntityFrameworkCore.SqlServer.Design too, it's there for scaffolding etc.

Azure Web App error 500 on Connection String

I've a simple ASP.NET Core 1.0 application which seeds some data in to SQL Server in Azure. I've 2 databases, one is for development and other is for Production (Azure SQL), I'm using Identity. When I run my application on my local machine, it works fine, but when I deploy it I get the error 500 (Internal Server Error) with no further explanation here is my code.
if (CurrentEnvironment.IsDevelopment())
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
else if (CurrentEnvironment.IsStaging() || CurrentEnvironment.IsProduction())
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Environment.GetEnvironmentVariable("SQLCONNSTR_kConnectionString")));
}
If I change the environment setting from Production to Development using Azure Application Settings, it says that error is at Configuration.GetConnectionString("DefaultConnection") saying connection string cannot be null.
There is a special pattern to be used, when you want to set the Variables from Environmental Variables.
In Azure App Service you set up your connection string as "DefaultConnection" or "SQLCONNSTR_kConnectionString" respectively, but you need to use. So if your connection string is "DefaultConnection", your appsetting.json must look like this
"ConnectionStrings" : {
"DefaultConnection" : "..."
}
when you obtain it with Configuration.GetConnectionString("DefaultConnection") (alternatively you can get it via Configuration["ConnectionStrings:DefaultConnection"]).
If you load it from environmental variables, it's better to override the default connection string via
var builder = new ConfigurationBuilder()
.SetBasePath(hostEnv.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostEnv.EnvironmentName}.json", optional: true, reloadOnChange: true);
.AddEnvironmentVariables();
Then the environmental variables will automatically override your settings in the appsettings.json or appsettings.Production.json.
If you want to get it via Environment.GetEnvironmentVariable you need to make it Environment.GetEnvironmentVariable("ConnectionStrings__SQLCONNSTR_kConnectionString") on Linux and Environment.GetEnvironmentVariable("ConnectionStrings:SQLCONNSTR_kConnectionString") on Windows. It's on top of my head, but the convention was something similar to this
I might misunderstand what you meant by "Azure SQL", but if you're using hosted Azure DB, then the prefix for the environment variable will be SQLAZURECONNSTR_ instead of SQLCONNSTR_. The latter would be used, for example, if you have SQL on your own VM in Azure (IaaS).
Also, it may help you to break apart your combined line of:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Environment.GetEnvironmentVariable("SQLCONNSTR_kConnectionString")));
into separate lines, something like the following, then you might be able to diagnose the issue a bit easier.
var envConnectionString = Environment.GetEnvironmentVariable(...);
if (string.IsNullOrEmpty(envConnectionString))
{
// log error, throw exception, etc
}
else
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(envConnectionString)));
}
Thank for your respond, actually I resolved my issue. Since ASP.NET Core RC 2, we have to use
"publishOptions": {
"include": [
"wwwroot",
"Views",
"Areas/**/Views",
"appsettings.json",
"web.config"
] },
options in project.json in order work them. :)

Resources