this is the first time I see someone who stores the database name in the request headers!. You should consider refactoring your way of storing that, as you don't want to expose any part of your connectionString to the public.
as an alternative, you can create an entry database that stores the database names, and users, each user would mapped to one database and give it a unique security stamp. Use that security stamp to get the stored database name. and reinitiate the context with a new connectionString.
for the IHttpContextAccessor part. You can register IHttpContextAccessor in the startup like this :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//register IHttpContextAccessor
services.AddHttpContextAccessor();
}
Now you can use it in your DbContext :
public class CheesedDbContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CheesedDbContext(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dbName = _context.HttpContext.Request.Headers["Db"];
if(string.IsNullOrWhiteSpace(dbName))
{
throw new ArgumentNullException(nameof(dbName));
}
optionsBuilder.UseSqlServer($"Server=.;Database={dbName};Trusted_Connection=True;MultipleActiveResultSets=true;");
}
}
You can also use ActionFilter to work with the IHttpContextAccessor on each request. Like this :
public class SomeFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
//Get header
var requestHeaders = context.HttpContext.Request.Headers["somekey"];
// do some sync actions ..etc.
base.OnActionExecuted(context);
}
public async override Task OnActionExecutionAsync(ActionExecutingContext context , ActionExecutionDelegate next)
{
//Get header
var requestHeaders = context.HttpContext.Request.Headers["somekey"];
// do some async actions ..etc.
await base.OnActionExecutionAsync(context, next);
}
}
then register it in the Startup :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.Filters.Add(new SomeFilterAttribute());
});
}
this can be applied on both AddControllers and AddControllersWithViews.
IServiceProviderto resolve the instance you want based on the header. If you already have a factory, it will be even easier. - there is no need to access the header in startup.cs, and as Darem mentioned: there is none.