Efcore: Enable calling Database.Migrate upon model configuration completion

Created on 30 May 2019  路  5Comments  路  Source: dotnet/efcore

Hi,

Where is the right place to call Database.Migrate post model configuration?

I tried calling it in the OnConfiguring method but an InvalidOperationException is thrown, saying:

An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

I'm working on a multi-tenant project where each tenant has a dedicated DB and connection string (provided dynamically by an external DI-provided service from an external DB).

Here's what my DbContext looks like:

```c#
public class AppDbContext : IdentityDbContext
{
readonly IAppConnectionStringProvider _ConnStringProvider;

public AppDbContext(DbContextOptions options) : base(options)
{
}

public AppDbContext(DbContextOptions options,
IAppConnectionStringProvider connStringProvider) : this(options)
{
_ConnStringProvider = connStringProvider;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//TODO might be redundant
if (optionsBuilder.IsConfigured)
return;

optionsBuilder.UseSqlServer(_ConnStringProvider.ConnectionString);

Database.Migrate();

}
}
```

What IAppConnectionStringProvider does, is it retrieves the tenant data and its connection string using a different CatalogDbContext that maintains the tenant data.

In reference to @bricelam's comment, I'm requesting guidance on where to call Database.Migrate.

Further technical details

EF Core version: 2.2.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer

closed-question customer-reported

Most helpful comment

Program.Main() is a better place than Startup.Configure(). BUT we discourage using Migrate() at all during startup. It's typically part of deployment, not startup. Creating an admin page to create/update the database after deployment is another good option.

Be careful with database permissions. Most users SHOULDN'T be able to drop tables, etc.

All 5 comments

It should go in your application startup logic.

@smitpatel
At the startup stage, the connection string isn't known yet. The connection string is retrieved from a different DB with a different DbContext, using an injected service.
I've updated my original post, was indeed not clear enough.

@bricelam may have better idea.
I put following in Configure method inside startup.cs and it initialized the database before running the app. You can achieve similar by putting in Program.Main method too.
C# using (var scope = app.ApplicationServices.CreateScope()) { using (var db = scope.ServiceProvider.GetService<ApplicationDbContext>()) { db.Database.Migrate(); } }

Program.Main() is a better place than Startup.Configure(). BUT we discourage using Migrate() at all during startup. It's typically part of deployment, not startup. Creating an admin page to create/update the database after deployment is another good option.

Be careful with database permissions. Most users SHOULDN'T be able to drop tables, etc.

@smitpatel

Either Main or Startup won't work for me, because the Tenant is not known yet, and additionally, there may be new tenants added.

@bricelam

Creating an admin page to create/update the database after deployment is another good option.

Sounds like what I'm gonna do. Gonna migrate/generate the tenant DBs on demand, as they're created by admin.

Thank you both!

Was this page helpful?
0 / 5 - 0 ratings