[EN] Detecting if the application is running to create migrations

Challenges with Scoped Dependencies in DbContext

In Entity Framework Core, DbContext is often used to interact with the database. However, in complex scenarios like multi-tenant applications, you might need to inject scoped services such as TenantProvider into your DbContext. This can create challenges when generating migrations because the application may not be able to resolve these dependencies during the migration process. However, I have a solution to this problem.

See the following class as an example:

public class TenantProvider : ITenantProvider {
     public TenantProvider(IHttpContextAccessor httpContextAccessor) {...}
}
...
public class ApplicationDbContext : DbContext
{
    private readonly ITenantProvider _tenantProvider;

    // Constructor for runtime
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, ITenantProvider tenantProvider)
        : base(options)
    {
        _tenantProvider = tenantProvider;
    }
}

Solution

To address this issue, you can add an additional constructor to your TenantProvider where you manually inject the dependencies. Here’s how you can do it:

public TenantProvider(Tenant tenant)
{
     Tenant = tenant;
}

Next, use the following code to detect if the application is running through Entity Framework in your DI setup. This ensures that no request is made to capture data in the HttpContext.

var isMigration = Assembly.GetEntryAssembly()?.FullName?.StartsWith("ef") ?? false;

In conclusion, handling scoped dependencies in your DbContext, especially in complex scenarios like multi-tenant applications, can be challenging when creating migrations. By adding an additional constructor to manually inject dependencies and detecting if the application is running through EF Core, you can effectively manage these dependencies. This approach allows you to switch from scoped to singleton dependencies as needed, ensuring smooth migration creation. Alternatively, using the DesignTimeDbContextFactory is another viable, albeit more complex, solution.

Full code sample

Here is the full code sample:

var isMigration = Assembly.GetEntryAssembly()?.FullName?.StartsWith("ef") ?? false;

if (isMigration)
{
     services.AddSingleton<ITenantProvider>(_ => new TenantProvider(tenantLocal));
}
else
{
     services.AddScoped<ITenantProvider, TenantProvider>();
}