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?
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());
}
}