用户授权后如何更改已注册的(简单注入器)DbContext的连接字符串? [英] How to change registered (Simple Injector) DbContext's connection string after user's authorization?

查看:96
本文介绍了用户授权后如何更改已注册的(简单注入器)DbContext的连接字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Simple Injector构建ASP.NET Core Web Api,并遇到以下问题:每个用户的数据都存储在其各自的库中(但具有相同的架构),只有当他授权自己后,该数据才知道.所以...只有在用户授权后,我才能给DbContext正确的连接字符串.最好的方法是什么?

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?

现在,我将连接字符串存储在HttpContext自定义声明中,并在DbContext的OnConfiguring方法中使用静态帮助程序类对其进行引用.

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.

助手:

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

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

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...

推荐答案

我的一般建议是

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.

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

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

这使您可以为当前请求检索正确的MyDbContext.一个实现可能如下所示:

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

使用此代码,您无需注入MyDbContext到使用者,而是注入IDbContextProvider.由于IDbContextProvider已注册为Singleton,因此它的使用者也可以成为Singletons.尽管如此,当他们调用IDbContextProvider.Context时,将为当前请求/范围返回正确的MyDbContext.

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.

这篇关于用户授权后如何更改已注册的(简单注入器)DbContext的连接字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆