从 BackgroundService 创建 DbContext 租户时配置它
Posted
技术标签:
【中文标题】从 BackgroundService 创建 DbContext 租户时配置它【英文标题】:Configure DbContext tenant when creating it from BackgroundService 【发布时间】:2021-05-01 03:00:33 【问题描述】:我有一个 ASP.NET Core 3.1 应用程序,它使用现有的 Postgres 数据库,其中每个租户都存储在单独的架构中。这在 HTTP 请求中效果很好,租户标识符存储在请求中,在我的 DbContext 中,OnConfiguring
方法中有以下代码:
if (this._httpContextAccessor.HttpContext?.Items["tenant"] != null)
string tenant = this._httpContextAccessor.HttpContext.Items["tenant"].ToString();
builder.SearchPath = tenant;
else
throw new InvalidOperationException("The defined tenant does not exist. Cannot create DB Context");
这会针对当前请求调整租户的 Postgres 搜索路径。
我现在正在尝试添加一个也需要使用数据库的后台服务。我的后台服务使用 IServiceProvider,我尝试按如下方式创建我的 DbContext,由于我使用的多租户实现,这显然不起作用:
using var scope = this.serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetService<MyDbContext>();
后台服务没有 HTTP 请求那样的租户,它需要自己处理这方面并生成具有不同租户的 DbContext。
我不确定如何以某种方式调整我的上下文,以便我可以创建具有不同租户的实例。或者这是否真的是我应该做的,或者是否有更好的方法来处理这个问题。我知道通过 Postgres 架构的多租户不是 ASP.NET Core 的典型案例,但我现在无法更改。
当我需要从 HTTP 请求和与特定租户无关的后台服务中使用 DbContext 时,我如何以及应该如何以及应该将 DbContext 与基于 Postgres 架构的租户一起使用(但例如,循环通过租户以在每个租户上执行工作)租户)?
【问题讨论】:
不要使用HttpContext
来获取租户ID,而是做一个你可以控制的服务。该服务可以使用HttpContext
,但如果您告诉它,它将使用其他东西。
我认为你应该用单一模式和复合键管理多租户系统,每个表都有租户 ID
【参考方案1】:
一种解决方案是不直接使用HttpContext
来获取租户ID。相反,创建一个您可以控制和注入的服务。该服务可以使用HttpContext
,但如果您告诉它,它将使用其他东西。例如:
(注意:所有这些都是手动输入的,未经测试,但应该让您了解它是如何工作的)
public interface ITenantService
string OverrideTenantId get; set;
string GetTenantId();
public class TenantService : ITenantService
private readonly IHttpContextAccessor _httpContextAccessor;
public string OverrideTenantId get; set;
public TenantService(DbContextOptions options, IHttpContextAccessor httpContextAccessor)
: base(options)
_httpContextAccessor = httpContextAccessor;
public string GetTenantId()
if(!string.IsNullOrEmpty(OverrideTenantId))
return OverrideTenantId;
return _httpContextAccessor.HttpContext.Items["tenant"].ToString();
调整您的 DbContext:
public class MyContext : DbContext
private readonly ITenantService _tenantService;
public MyContext(ITenantService tenantService)
_tenantService = tenantService;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
var tenantId = _tenantService.GetTenantId();
if(string.IsNullOrEmpty(tenantId))
throw new InvalidOperationException("The defined tenant does not exist...");
builder.SearchPath = tenantId;
base.OnConfiguring(optionsBuilder);
将服务添加到您的 DI 容器中:
services.AddScoped<ITenantService, TenantService>();
并在你的后台服务中像这样使用它:
using var scope = this.serviceProvider.CreateScope();
// This will be the same instance that the DbContext receives
var tenantService = scope.ServiceProvider.GetService<ITenantService>();
tenantService.OverrideTenantId = "whatever";
var context = scope.ServiceProvider.GetService<MyDbContext>();
请注意,如果您需要切换到不同的租户,您可能需要创建一个新的DbContext
和范围。
【讨论】:
谢谢,这似乎有效。我缺少的部分是如何构建服务,以便我可以修改租户并将该版本的租户服务注入我的 BackgroundService。以上是关于从 BackgroundService 创建 DbContext 租户时配置它的主要内容,如果未能解决你的问题,请参考以下文章
从asp.net core 2.1中的控制器访问BackgroundService
android localytics 4.5.1 崩溃 NoClassDefFoundError com.localytics.android.BackgroundService
如何在 BackgroundService 获取 ASP.NET Core 启动地址
使用 ASP.NET Core 6 Minimal API 在 Azure 中停止 Web 应用程序时未调用 BackgroundService StopAsync
如何在 BackgroundService 类中制作 ServiceBusClient 和 ServiceBusProcessor DisposeAsync?