通过上下文覆盖将基于ef的应用程序转换为多租户
本文关键字:程序转换 应用 ef 上下文 覆盖 | 更新日期: 2023-09-27 18:11:29
我有一个实体框架,基于代码优先的应用程序,我必须使多租户,也就是说,有大约六个"顶级"实体,现在需要引用特定的租户ID。(当我们达到100个用户时,不,我们不打算维护单个模式,所以请不要建议这样做。:))
使用像EF这样的面向对象的数据访问抽象,我试图想象我如何能够达到一个不需要更改dbcontext之外的任何底层代码来实现此工作的地方。基本上,我想用这些作为我的成功标准:
- 不需要更改现有的数据访问代码。有很多,很多都是程序性的和重复的。不幸的是,没有存储库类,尽管我很想做到这一点,但我不得不推迟技术债务。
- 查询根据租户ID过滤顶层对象。例如,现有代码获得context.Members。Where(x => x. isawesome),但也神奇地过滤到租户ID等于租户ID的地方(租户上下文对每个请求都可用,并且可用于注入)。
- 添加顶级实体也分配租户ID。换句话说,代码做了类似context.Members.Add(newEntity)和newEntity的事情,newEntity神奇地将其TenantID属性设置为通过注入的组件可用的ID。
似乎可以使用实体类本身来设置租户ID(还没有考虑过对其进行注入,有些东西卡在那里),但我不确定如何最好地添加用于查询的额外过滤器。
我不确定完全不修改代码就能做到这一点,但以下是我的做法。首先,为您的多租户实体引入一个接口(我假设它们每个都有TenantID
属性,映射到数据库列):
public interface IMultiTenantEntity {
int TenantID { get; set; }
}
然后在所有实体中实现它。它们是自动生成的,但是是局部的,所以只需执行:
public partial class YourEntity : IMultiTenantEntity {}
现在,要在保存时填充此属性,在您的上下文中重写SaveChanges
(同样,它是自动生成的,但部分,所以您不必触摸自动生成的代码):
public partial class YourContext : DbContext
{
private int _tenantId;
public override int SaveChanges() {
var addedEntities = this.ChangeTracker.Entries().Where(c => c.State == EntityState.Added)
.Select(c => c.Entity).OfType<IMultiTenantEntity>();
foreach (var entity in addedEntities) {
entity.TenantID = _tenantId;
}
return base.SaveChanges();
}
public IQueryable<Code> TenantCodes => this.Codes.Where(c => c.TenantID == _tenantId);
}
以上我假设您已经以某种方式将当前租户id注入_tenantId
字段。
然后,对于每个实体集,添加单独的属性,该属性将返回由TenantID
过滤的该集(再次在您的上下文的部分类中):
public IQueryable<YourEntity> TenantYourEntities => this.YourEntities.Where(c => c.TenantID == _tenantId);
现在您需要做的就是找到对YourEntities
集合的所有引用(用右键单击>找到所有引用),并用对TenantYourEntities
的引用替换它们。然后,您的所有查询将被TenantID
过滤,而无需太多工作。当然,不要在使用DbSet修改实体(Db.YourEntities.Add(...)
)的地方替换引用。
从技术上讲,只要在上下文实例化时知道租户ID,您就可以简单地在上下文中设置一个带有该值的字段,并在重载中引用该字段。例如,您可以从应用程序设置中读取它。右键单击项目并选择"属性"。然后,转到"设置"选项卡,打开它。把你在开发中用到的东西都写进去。然后,为每个租户向项目添加配置,并编辑配置转换以将其切换为适当的值。那么,在你的DI初始化中,你可以读取这个设定值并将其作为常量注入。
如果租户是在运行时设置的,比如通过URL的一部分,那么使用DI就会变得有点困难。上下文通常是请求作用域的,所以这不是真正的问题。然而,DI初始化是通常不在请求管道中完成。此时,您可能只需要手动设置该值,或者在代码中创建上下文,其中是请求管道的部分,例如控制器。