Reputation: 461
I am using Azure .NET core WebApp, MVC, Entity Framework, scaffolded in an existing external MS SQL database.
I register the DB in the startup ConfigureServices like this:
services.AddDbContext<MyDbContext>(options => options.UseSqlServer());
And it works fine so long as I set the connection string in the MyDbContext.cs OnConfiguring() method like this:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=10.10.10.10;Database=MyDb;user id=username;password=password;");
}
Then I can simply in a Controller say:
private readonly MyDbContext _context;
public HomeController()
{
_context = new MyDbContext();
}
public IActionResult Index()
{
var item = _context.TableName.FirstOrDefault(d => d.Id > 0);
}
And this works fine - data flows in.
My problem is that I need to change the SQL connection string depending on what hostname is connecting. So, either in the startup ConfigureServices() I can pass the connection if I can establish the hostname being used, or in the DB's MyDbContext.cs file's OnConfiguring() method.
I can get the hostname only from the httpcontext, and that isn't available to query in the startup so far as I can tell?
If I try and inject it into the context.cs DB file, like this:
public partial class MyDbContext : DbContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyDbContext()
{
}
public MyDbContext(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor; // For context/url
}
public MyDbContext(DbContextOptions<MyDbContext> options, IHttpContextAccessor httpContextAccessor)
: base(options)
{
_httpContextAccessor = httpContextAccessor; // For context/url
}
Then the startup ConfigureServices line services.AddDbContext can't pass the httpcontext (it doesn't exist at that point?) none of the constructor methods match - I can't inject the IHttpContextAccessor httpContextAccessor into the DB context method no matter how I try!
services.AddDbContext<MyDbContext>(options => options.UseSqlServer("some-connection-string")); // Doesn't pass httpcontext
services.AddDbContext<MyDbContext>()); // Doesn't pass httpcontext
services.AddDbContext<MyDbContext>(is there way to pass it?);
It doesn't seem to work like injecting it into Controllers, which does work fine as there's no constructor there...
Any ideas on how I can find the hostname and change the SQL connection string given this setup?
For various reasons this needs to be a single WebApp that multiple domains use by the way.
After a sleepless night I woke up and decided to simply connect to all (three) databases I need and then decide which context to use in the controller, as I have the httpcontext there to decide what host is connecting. It's not ideal, but this is a low overhead WebApp so I'm happy enough to go with it like that. I think perhaps there is/was a solution out there though...
Upvotes: 1
Views: 988
Reputation: 7149
Here's the approach I've been using for multi-tenant solutions (where clients have their own database).
Create your context with this constructor signature (no need for OnConfiguring
):
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options) { }
}
In your Startup, you need to register your context and we can now add a resolver to get the correct connection settings:
services.AddHttpContextAccessor();
services.AddDbContext<MyDbContext>();
// This registers DbContextOptions<MyDbContext> which will be called when a new
// instance of MyDbContext is created. You could set breakpoints in this method.
services.AddScoped(sp =>
{
var context = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
// your logic to determine connection string or EF provider
// ...
var builder = new DbContextOptionsBuilder<MyDbContext>();
builder.UseSqlServer("connection string");
return builder.Options;
});
Upvotes: 5
Reputation: 4177
The HttpContextAccessor
is no longer wired up by default.
Make sure you add the service in your ConfigureServices
method. That way you'll be able to use the HttpCOntext inside your DbContext classes.
Your DbContext as posted in this question would be fine.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>(options => options.UseSqlServer());
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
...
}
Upvotes: 0