Rob
Rob

Reputation: 163

How can I use a single controller with different DBContexts?

I have ~70 databases which are all exactly the same structure. What I am attempting to do is to create a quick Blazor (WASM/hosted) site on .net 6 which can access all of these sites using EF. Blazor is probably unimportant because all of the magic is happening in the 'hosted' portion which is really just a .Net API project.

So far, I have created a CommonDBContext which inherits DBContext and then individual DBContexts for each database which inherit CommonDBContext.

CommonDbContext.cs

 public partial class CommonDbContext : DbContext
    {
        public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options)
        {
        }

        protected CommonDbContext(DbContextOptions options) : base(options)
        {
        }
+2600 lines of EF scaffolding code

DB1-Context.cs

public partial class DB1Context : CommonDbContext
    {
        public DB1Context(DbContextOptions<DB1Context> options)
            : base(options)
        {
        }
    }

These various context are then injected in Program.cs

builder.Services.AddDbContext<DB1Context>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DB1"));
});

builder.Services.AddDbContext<DB2Context>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DB2"));
});

I am not convinced this is a wonderful solution for 70 databases but it is a start. This approach seems to work perfectly - I can create a controller and whichever DB context I use, it seems to pull from the proper database.

Here's the problem

How can I select which DBContext I want to use in the controller? Building ~70 duplicate controllers seems very wrong but I haven't figured out how to pass in the DBContext from my request to the controller.

DopplegangerController.cs

[Route("api/[controller]")]
    [ApiController]
    public class DopplegangerController: ControllerBase
    {
        private DB1Context_context;  // <-- RIGHT HERE
        public DopplegangerController(DB1Contextcontext)
        {
            _context = context;
        }


        // GET: api/<DopplegangerController>
        [HttpGet]
        public List<Stuffs> Get()
        {
            return _context.Stuffs.ToList();
        }

Upvotes: 2

Views: 170

Answers (1)

Kilarn123
Kilarn123

Reputation: 733

In you Program.cs when configuring your services, you can give a function that will fetch the connection string depending on your settings. This function will be call each time the DbContext needs to be injected.

builder.Services.AddDbContext<CommonDbContext>(getSQLOptions, ServiceLifetime.Transient);
void getSQLOptions(IServiceProvider serviceProvider, DbContextOptionsBuilder dbContextBuilder)
{
    YourSettings? settings = serviceProvider.GetService<YourSettings>();
    IHttpContextAccessor? httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
    
    /* Get your connectionString from your settings and HttpContext (Can be headers, query param or whatever) */
    
    dbContextBuilder.UseSqlServer(connectionString);
}

You can then just inject your unique CommonDbContext in your Controller and use it. You don't need the child DbContext anymore

Upvotes: 2

Related Questions