1

I need to create DbContext for each lifetime scope and the database name is not fixed, for each lifetime scope I want to have the DB from a different local store, to get the local store's value I need a key and I want to get that key from HTTPContext or IHttpContextAccessor. So, how can I get the value of the HTTP header in startup.cs?

N.B: I don't have any plan to send DB name through the header, this code is just an example, I'll update my code using key-value and some store service for the database name. The given code example always execute for each request if the request reference to the database.

services.AddDbContext<CheesedDbContext>((options =>
{
    options.UseSqlServer($"Server=.;Database={DB};Trusted_Connection=True;MultipleActiveResultSets=true;");
            
});
4
  • 1
    There is no HTTPContext in the Startup.cs. You can add a DbFactory and pass the HTTPContext to the factory and the factory creates a Db instance. Commented Jul 14, 2021 at 9:26
  • I have a dbfactory class, but there is no way to resolve this problem? i want to get the facility of InstancePerLifetimeScope() of Autofac. Commented Jul 14, 2021 at 9:29
  • There are a couple of ways to do this but easiest would be to use IServiceProvider to 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. Commented Jul 14, 2021 at 9:32
  • @Darem please check my answer. i solved it. Commented Jul 14, 2021 at 10:08

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

@iSRS I appreciate your answer, but I don't have any plan to send the DB name through the header, I do have a plan using the key and some backend code, the question just for as an example I'll edit soon. Thanks.
1

I solved it using services.BuildServiceProvider()!

services.AddDbContext<CheesedDbContext>(options =>
{
    var serviceProvider = services.BuildServiceProvider();
    var db = serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext?.Request?.Headers["Db"];
    options.UseSqlServer($"Server=.;Database={db.Value};Trusted_Connection=True;MultipleActiveResultSets=true;");
});

1 Comment

why negative voting?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.