如何在用户授权后更改已注册(简单注入器)DbContext 的连接字符串?



我正在使用简单注入器构建 ASP.NET 核心Web Api,并遇到以下问题: 每个用户的数据都存储在他各自的数据库中(但具有相同的架构(,只有在他授权自己之后才能知道。 所以...只有在用户授权后,我才能DbContext它是正确的连接字符串。实现这一目标的最佳方法是什么?

现在,我将连接字符串存储在HttpContext自定义声明中,并使用DbContext的 OnConfigure 方法中的静态帮助程序类来引用它。

帮助程序:

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;
}
}
}

我已经注册了这样:

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>());
}

毕竟,我像这样调用连接字符串:

public class BaseDbContext : DbContext
{
public BasetDbContext(DbContextOptions options)
: base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (WebHelper.ConnectionString != null)
{
optionsBuilder.UseSqlServer(WebHelper.ConnectionString);
}
}
}

而且我对这种方法并不完全满意,所以我想问是否有人有更好的主意......

我的一般建议是将运行时数据与对象图组合分开。这意味着对象图的所有内容都应仅由组件组成,而包含(可变(运行时数据的所有内容都不应使用构造函数注入到对象图中,而应通过现有对象图组件的方法调用进行流动。

根据我的经验,这种分离导致了一个更容易绘制图的简化模型,以及一个更容易验证正确性的对象图。

在您的问题中,有两个明显的运行时数据:DbContext和连接字符串。在典型的应用程序中,只有一个连接字符串值,它是一个常量。常量可以安全地注入到对象图中。当连接字符串根据运行时条件更改时,这是不同的,因为它会导致连接字符串本身成为运行时数据。

不要直接注入运行时数据,而是通过方法调用提供对运行时数据的访问。一个明显的解决方案是为应用程序提供一个抽象,该抽象在调用方法时返回运行时数据。例如:

public interface IDbContextProvider
{
MyDbContext Context { get; }
}

这允许您检索当前请求的正确MyDbContext。实现可能如下所示:

public class DelegateDbContextProvider : IDbContextProvider
{
private readonly Func<MyDbContext> provider;
public DelegateDbContextProvider(Func<MyDbContext> provider)
=> this.provider = provider;
public MyDbContext Context => this.provider();
}

这允许您按如下方式注册它:

var contextProducer = Lifestyle.Scoped.CreateProducer<MyDbContext>(
() => new MyDbContext(WebHelper.ConnectionString), container);
container.RegisterInstance<IDbContextProvider>(
new DelegateDbContextProvider(contextProducer.GetInstance));

使用此代码,不是向使用者注入MyDbContext,而是注入IDbContextProvider。由于IDbContextProvider被注册为Singleton,其消费者也可以成为Singletons。尽管如此,当他们调用IDbContextProvider.Context时,会为当前请求/范围返回正确的MyDbContext

最新更新