如何根据从另一个 dbcontext 获得的数据库名称加载动态 dbcontext? [英] How to load dynamic dbcontext based on database name obtained from another dbcontext?

查看:23
本文介绍了如何根据从另一个 dbcontext 获得的数据库名称加载动态 dbcontext?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 ASP.NET Core 5 开发 Web API 应用程序,目标是支持多租户.每个租户都有自己的数据库.

I'm developing a Web API application with ASP.NET Core 5 with the goal of being multitenant capable. Each tenant will have their own database.

在这个应用程序中,我将有两个 DbContext:

In this application I will have two DbContext:

  • DBContextMaster - 此上下文数据在 appsettings.json 中将是静态的.它将从 Web API 获取发出请求的租户的信息,并获取为该特定客户存储的数据库名称

  • DBContextMaster - this context data will be static in appsettings.json. It will get the information of the tenant making the request from the Web API and get the name of the database stored for that specific customer

DBContextTenant - 此上下文将在 appsettings 中有一个连接字符串,类似于:

DBContextTenant - this context will have a connection string in appsettings similar to this:

  "DynamicTenant": "Data Source=server;Initial Catalog={dbName};Integrated Security=False.

{dbName} 必须被替换才能使用正确的数据库.

{dbName} must be replaced in order for the correct database to be used.

我有一些关于如何做到这一点的问题,但我的想法是:

I have some questions about how to do this, but my idea would be:

  • 随请求发送租户代码或某种 APIKEY
  • 验证租户是否存在,然后替换DynamicTenant连接字符串(部署某种缓存以避免连续查询检查是否存在相同的租户)
  • send the tenant code or a kind of APIKEY with the request
  • validate if the tenant exists and then replace the DynamicTenant connection string (deploy some kind of cache to avoid successive queries checking if the same tenant exists)

由于我刚刚开始使用 ASP.NET Core,我想知道如何以及在何处动态加载租户上下文.

As I am just starting with ASP.NET Core, I would like to know how and where I could dynamically load the tenant context.

更新:

这段代码显然对我很有用.

This code will apparently serve me well.

   services.AddDbContext<DbContextTenant>((serviceProvider, dbContextBuilder) =>
        {
            var connectionStringPlaceHolder = Configuration.GetConnectionString("DynamicTenant");
            var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
            var dbName = httpContextAccessor.HttpContext.Request.Headers["tenantId"].First();
            var connectionString = connectionStringPlaceHolder.Replace("{dbName}", dbName);
            dbContextBuilder.UseSqlServer(connectionString);

        });
        

但现在我有另一个问题.运行项目时没有错误,但是当尝试使用此 dbcontext 添加新控制器时,出现以下错误未将对象引用设置为对象的实例".当我尝试为项目启用迁移时出现同样的错误.

But now I have another problem. When running the project there is no error, but when trying to add a new controller using this dbcontext, the following error occurs "Object reference not set to an instance of an object". Same error when I try to enable Migrations for the project.

我认为错误正在发生,因为在开发时没有 http 上下文.

I assume the error is occurring because at development time there is no http context.

可以采取哪些实施来解决这些问题?

What implementation can be done to get around these problems?

推荐答案

随请求发送租户代码或某种APIKEY

你做得对.

有多种方法可以从用户那里获取租户,您可以使用标题,您可以使用查询字符串,您可以使用主机(例如 IP 地址或域名)或所有操作的参数,例如 API 密钥.

there are ways of getting tenants from users, you can use headers, you can use a query string, you can use a host (for example IP address or domain name) or a parameter to all your actions for example an API key.

但我建议您使用主机或标头,这样您就可以为此添加一个中间件来监控所有请求.

but I suggest you, use host or header, so you can add a middleware for that to monitor all requests.

看看这个例子:

public class TenantMiddleware
{

    private readonly RequestDelegate _next;

    public TenantMiddleware (RequestDelegate next)
    {
        _next = next;
    }

    public TenantMiddleware (RequestDelegate next)
    {
        _next = next;
    }


    public async Task Invoke (HttpContext context)
    {
        context.Request.Headers.TryGetValue(_tenantKey, out StringValues host);
        var tenantHolder = context.RequestServices.GetRequiredService<ITenantHolder>();
        tenantHolder.SetTenant(_tenantKey);
    }

}

tenantHolder 是一个范围服务,它具有每个请求的 tenantId ,您可以从中获取数据.它只有一个变量,可以为您提供租户,您也可以将其用作上下文的工厂.

tenantHolder is a scoped service that has the tenantId for each request and you can get the data from that. it just has a variable that gives you the tenant, you can also use it as a factory for your contexts.

您可以在此存储库中找到更多详细信息:租户提供商

you can find more details on this repo: TenantProvider

这篇关于如何根据从另一个 dbcontext 获得的数据库名称加载动态 dbcontext?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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