How to use code first migrations with Azure App Service - entity-framework

When I run locally I run the commands below manually and then package and publish the app onto my IIS server.
Add-Migration Initial
Update-Database
When I want to publish to an azure appservice will these commands run automatically? If so how does it know to use a different ConnectionString when I publish it to azure?
I added the connectionString for azure in appsettings.json but I don't understand how I can tell my controllers etc to use that when I publish to azure AppServices
"ConnectionStrings": {
"AzureTestConnection": "Data Source=tcp:xxxxxx-test.database.windows.net,1433;Initial Catalog=xxxxx;User Id=xxx#yyyy.database.windows.net;Password=xxxxxx",
"NWMposBackendContext": "Server=(localdb)\\mssqllocaldb;Database=NWMposBackendContext-573f6261-6657-4916-b5dc-1ebd06f7401b;Trusted_Connection=True;MultipleActiveResultSets=true"
}
I am trying to have three profiles with different connection strings
Local
Published to AzureApp-Test
Published to AzureApp-Prod

When I want to publish to an azure appservice will these commands run automatically?
EF does not support Automatic migrations, you may need to manually execute Add-Migration or dotnet ef migrations add for adding migration files. You could explicitly execute the command to apply the migrations, also you could apply migrations in your code.
And you could add the following code in the Configure method of Startup.cs file:
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
}
I am trying to have three profiles with different connection strings
You would dynamically choose a connection string based on Environment, so here is main steps, you could refer to it.
Set the ASPNETCORE_ENVIRONMENT value to azure in webapp>property>debug.
2.Follow ASP.NET Core MVC with Entity Framework Core to get started.
3.Set the appsetting.json with your two connection string.
{
"ConnectionStrings": {
"DefaultConnection": "connectiondefault",
"azure": "connectionazure"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
Note:You could also set the connectionstring in database on portal to here, then you could test it in local and could use debug to troubleshooting.
Also, you could try to test with one connectionstring to ensure you have no problem with connecting to database.
4.Enable Developer exception page by using app.UseDeveloperExceptionPage(); and the app.UseExceptionHandler methods in your startup class which would display the errors.
public Startup(IHostingEnvironment env)
{
Configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
HostingEnvironment = env;
}
public IConfigurationRoot Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
if (HostingEnvironment.IsDevelopment())
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
else
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("azure")));
}
services.AddMvc();
}
For more details, you could refer to this thread.

Related

Sending parameter by docker-compose does not work on appsettings in .NET6

I have a docker-compose with environment variables for the ConnectionString :
services:
nameservice:
...
environment:
ConnectionStrings__mysqlDatabase: "Server=db;Uid=root;Pwd=password;"
I have on the .NET side in a console program, an AppSettings file (.NET6):
{
"ConnectionStrings": {
"mysqlDatabase": "Server=localhost;Uid=root;Pwd=password;"
}
}
and my program.cs :
IConfiguration Config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
ConnectionStringConstant.mysqlDatabase = Config.GetConnectionString("mysqlDatabase");
my problem is that in docker after creating a container, it keeps the value in AppSettings (which is not good for production) and it does not take the value passed by the docker-compose file.
Can you help me?
When building configuration manually (without using hosting) setup for environment variables support is required:
var configurationRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
Read more:
Configuration in .NET

Failed to load API definition Fetch Error, when deployed but fine on localhost

I am upgrading my api from netcore2.1 to 3.1
When I run on localhost the UI works fine.
When I deploy via Azure DevOps and go to the myapplication/myapi/swagger.html url I get
Failed to load API definition
Fetch Error
Service Unavailable /myapi/swagger/v1/swagger/json
Yet I can see the json at
myapplication/myapi/swagger/v1/swagger.json
I have the following
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger(c =>
c.RouteTemplate = "myapi/swagger/{documentName}/swagger.json"
);
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/myapi/swagger/v1/swagger.json", "Versioned API v1.0");
c.RoutePrefix = "myapi/swagger";
});
return app;
}
I am using
Swashbuckle.AspNetCore (5.2.0)
I found the following worked.
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger(c =>
c.RouteTemplate = "myapi/{documentName}/swagger.json"
);
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("./v1/swagger.json", "Versioned API v1.0");
c.RoutePrefix = "myapi";
});
return app;
}
The docs state
If using directories with IIS or a reverse proxy, set the Swagger
endpoint to a relative path using the ./ prefix. For example,
./swagger/v1/swagger.json. Using /swagger/v1/swagger.json instructs
the app to look for the JSON file at the true root of the URL (plus
the route prefix, if used). For example, use
http://localhost://swagger/v1/swagger.json instead
of
http://localhost:///swagger/v1/swagger.json.
However unfortunately my solution doesn't work with Autorest.
Thus I asked another question

After Deploying, ASP.NET application showing Internal server error

I deployed my ASP.NET application to a remote server with a hosting company, and when i try to send data from Postman, i get the internal server error with no definite error message. I have set custom error mode to off in the web config file. please can anyone help me? I have checked for several solutions but nothing.
PS: i am new to ASP.NET deployment with other companies apart from Azure
In this case, you should log error to file to see what issues in deployment mode.
This way i implemented global error log.
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Log Critical errors
// You can use log4net library and configure log folder
}
}
In WebApiConfig.cs file you register it.
public static void Register(HttpConfiguration config)
{
// .....
config.Filters.Add(new ExceptionHandlingAttribute());
}

web.config in ASP.NET Core 2

I just started learning ASP.NET Core 2 MVC application.
After taking a new project, I don't see any web.config file in solution explorer.
Any help?
Sorry if this a foolish question.
Not just Asp.Net Core 2.x no longer uses web.config, even the Asp.Net Core no longer uses it.
The configuration now is part of the application startup procedure, in Startup.cs. And there is an application setting file called appsettings.json where you can put all your configuration values there.
You can read setting values like this:
appsettings.json
{
"Recaptcha": {
"SiteKey": "xxxx-xxxx",
"SecretKey": "xxxx-xxxx"
}
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; private set; }
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
...
// Configure Google Recaptcha
// reading values from appsettings.json
services.AddRecaptcha(new RecaptchaOptions
{
SiteKey = this.Configuration.GetValue<string>("Recaptcha:SiteKey"),
SecretKey = this.Configuration.GetValue<string>("Recaptcha:SecretKey")
});
}
}
Also in .Net Core projects, there is .csproj file. You can right click a project and click Edit <project>.csproj.
web.config will be generated after you publish your web projects.

EF Code First DbMigration without nuget

How to migrate DB without nuget? It is not possible to use Visual Studio with nuget in production environment. Currently, many examples only teach us to use Visual Studio with nuget.
How to use the generated DbMigration classes?
The easiest way is:
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<MyDbContext,
MyDbMigrationsConfiguration>());
This will run the migrations when initializing the DbContext.
You can also force the execution manually:
var migrator = new DbMigrator(new MyMigrationsConfiguration());
migrator.Update();
(I believe you also have to set TargetDatabase on the configuration, but you can try)
Here are the options:
Use the migrate.exe command line tool that ships in our NuGet
package.
Use the MigrateDatabaseToLatestVersion initializer as
others have described.
Use the runtime API available from the
DbMigrator class.
You can migrate to the latest version using a Web.config setting - see this blog post by Rowan Miller:
If you are using Code First Migrations, you can configure the database to be migrated automatically using the MigrateDatabaseToLatestVersion initializer.
<contexts>
<context type="Blogging.BlogContext, MyAssembly">
<databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[Blogging.BlogContext,
MyAssembly], [Blogging.Migrations.Configuration, MyAssembly]], EntityFramework" />
</context>
</contexts>
Just swap your context class in here: the System.Data.Entity.MigrateDatabaseToLatestVersion is built-in to EF. This setting updates the old AppSettings version of the same idea.
To my mind this is the best way, because the question of which initializer to use is a configuration one really, and you want to be able to Web.config this, and ideally apply config transforms to work for your different environments.
You can do it using the EF Power Tools, there's the migrate.exe program that you can use to run migrations from the command prompt (post build for example). If you want to run migrations on production database you can also use the Update-Database command to generate SQL scripts from the migration classes, very useful if you need to pass through a DBA.
EF Power Tools are available on the Visual Studio Gallery and optionally here, check out this very useful video that, among other things, talks about the Update-Database command.
there is another solution :
Using DB = New SHAContext()
If DB.Database.Exists() Then
Dim migrator As New DbMigrator(New SHAClassLibrary.Migrations.Configuration())
For Each m In migrator.GetDatabaseMigrations()
Try
migrator.Update(m)
Catch ex As Exception
End Try
Next
End If
'DB.test()
End Using
I was looking for a way to control which migrations run explicitly in code without the need of a DbConfiguration class or automatic migrations enabled.
So i managed to create the following extension:
public static void RunMigration(this DbContext context, DbMigration migration)
{
var prop = migration.GetType().GetProperty("Operations", BindingFlags.NonPublic | BindingFlags.Instance);
if (prop != null)
{
IEnumerable<MigrationOperation> operations = prop.GetValue(migration) as IEnumerable<MigrationOperation>;
var generator = new SqlServerMigrationSqlGenerator();
var statements = generator.Generate(operations, "2008");
foreach (MigrationStatement item in statements)
context.Database.ExecuteSqlCommand(item.Sql);
}
}
As an example, having a migration like the following:
public class CreateIndexOnContactCodeMigration : DbMigration
{
public override void Up()
{
this.CreateIndex("Contacts", "Code");
}
public override void Down()
{
base.Down();
this.DropIndex("Contacts", "Code");
}
}
You could run it using your DbContext:
using (var dbCrm = new CrmDbContext(connectionString))
{
var migration = new CreateIndexOnContactCodeMigration();
migration.Up();
dbCrm.RunMigration(migration);
}