Inject DbContext when database name is only know when the controller action is called

asp.net-core asp.net-web-api c# entity-framework-core

Question

I have a WebAPI/EF Core back-end application with several possible client databases to connect to (each of the same schema, but only one at a time).

Knowledge of which database to connect to will not be known until a WebAPI controller is accessed. By that time, it will be too late to add the DBContext as an injectable object using the call to services.AddDbContext<>() in the ConfigureServices() method of the starup.cs file.

Since each instance of DbContext is isolated per request, is there anything wrong with instantiating the DbContext directly from the controller's action instead of passing it through using DI?

1
1
3/25/2017 1:18:12 PM

Accepted Answer

You can/should use an abstract factory to create the DbContext

public interface IDbContextFactory
{
    ApplicationContext Create();
}

public class DbContextFactory() : IDbContextFactory, IDisposable
{
    private ApplicationContext context;
    private bool disposing;

    public DbContextFactory(/* other dependencies here */)
    {
    }

    public ApplicationContext Create(string tenantId) 
    {
        if(this.context==null) 
        {
            // and the connection string and replace the database name in it
            // with the tenantId or whatever means you have to determine
            // which database to access
            string connectionString = ...;

            var dbContextBuilder = new DbContextOptionsBuilder();
            dbContextBuilder.UseSqlServer(connectionString);

            this.context = new ApplicationContext(dbContextBuilder);
        }

        return this.context;
    }

    public void Dispose(){
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing){
        if (disposing){
            disposing?.Dispose();
        }
    }
}

Register it

services.AddScoped<IDbContextFactory, DbContextFactory>();

and use it in your controllers as following

public class MyController : Controller
{
    private readonly IDbContextFactory contextFactory;

    public MyController(IMyContextFactory contextFactory)
    {
        this.contextFactory = contextFactory;
    }

    public Task<IActionResult> GetSomeData(string tenantId)
    {
        var context = contextFactory.Create(tenantId);

        return Ok(await context.Data.Where(...).ToListAsync());
    }
}
2
3/27/2017 10:51:31 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow