问题
I'm building ASP.NET Core Web Api with Simple Injector and have the following problem: The data for each user is stored in his individual base (but with the same schema), which is known only after he authorizes himself. So... I can give DbContext
it's proper connection string only after user's authorization. What is the best way to accomplish this?
For now I am storing the connection string in HttpContext
custom claim and am refering to it with a use of a static helper class in DbContext
's OnConfiguring method.
The helper:
public class WebHelper
{
private static IHttpContextAccessor _httpContextAccessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
private static HttpContext HttpContext
{
get
{
return _httpContextAccessor.HttpContext;
}
}
public static string ConnectionString
{
get
{
return _httpContextAccessor?.HttpContext?.User.FindFirst(CustomClaimTypes.ConnectionString)?.Value;
}
}
}
Which I have registered like this:
private SimpleInjector.Container _container = new SimpleInjector.Container();
public void ConfigureServices(IServiceCollection services)
{
_container.Register(() =>
{
return new BaseDbContext(SqlServerDbContextOptionsExtensions.UseSqlServer(new DbContextOptionsBuilder(),
"Data Source=127.0.0.1").Options);
}, Lifestyle.Scoped);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
WebHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
}
And after all I am calling the connection string like this:
public class BaseDbContext : DbContext
{
public BasetDbContext(DbContextOptions options)
: base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (WebHelper.ConnectionString != null)
{
optionsBuilder.UseSqlServer(WebHelper.ConnectionString);
}
}
}
And I am not exactly satisfied with this approach, so I want to ask if anyone has a better idea...
回答1:
My general advice is to keep runtime data separated from object graph composition. This means that everything that object graphs should only consist of components, and everything that consists of (mutable) runtime data, should not be injected into the object graph using constructor injection, but instead should flow through the method calls of the components of the existing object graph.
In my experience, this separation leads to a simplified model that is much easier to graphs, and an object graph that is easier to verify for correctness.
In your question, there are two obvious pieces of runtime data: the DbContext
and the connection string. In a typical application, there is only one connection string value, and it's a constant. Constants can be safely injected into object graphs. This is different when a connection string changes based on runtime conditions, since it results in the connection string itself become runtime data.
Instead of injecting the runtime data directly, provide access to the runtime data by means of method calls. An obvious solution is to provide the application with an abstraction that returns the runtime data when a method is called. For instance:
public interface IDbContextProvider
{
MyDbContext Context { get; }
}
This allows you to retrieve the proper MyDbContext
for the current request. An implementation might look as follows:
public class DelegateDbContextProvider : IDbContextProvider
{
private readonly Func<MyDbContext> provider;
public DelegateDbContextProvider(Func<MyDbContext> provider)
=> this.provider = provider;
public MyDbContext Context => this.provider();
}
This allows you to register it as follows:
var contextProducer = Lifestyle.Scoped.CreateProducer<MyDbContext>(
() => new MyDbContext(WebHelper.ConnectionString), container);
container.RegisterInstance<IDbContextProvider>(
new DelegateDbContextProvider(contextProducer.GetInstance));
With this code, instead of injecting a MyDbContext
into consumers, you inject an IDbContextProvider
. Since IDbContextProvider
is registered as Singleton
, its consumers might be able to become Singletons
as well. Still, when they call IDbContextProvider.Context
, the correct MyDbContext
is returned for the current request/scope.
来源:https://stackoverflow.com/questions/50890514/how-to-change-registered-simple-injector-dbcontexts-connection-string-after-u