通过上下文覆盖将基于EF的应用转换为多租户 [英] Convert EF-based app to multi-tenant by way of context overrides

查看:117
本文介绍了通过上下文覆盖将基于EF的应用转换为多租户的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个必须以多租户身份使用的基于代码框架的实体框架应用程序,也就是说,现在大约有六个顶级"实体需要引用特定的租户ID. (当我们达到100个用户时,不,我们不会维护单个模式,所以请不要提出这个建议.:))

I have an Entity Framework, code-first based app that I have to make multi-tenant, which is to say that there are about a half-dozen "top level" entities that now need to reference the specific tenant ID. (As we get to 100's of users, no, we're not going to maintain individual schema, so please don't suggest that. :))

通过对像EF这样的数据访问进行面向对象的抽象,我试图想象如何到达一个无需更改dbcontext之外的任何底层代码即可完成此工作的地方.本质上,我想将这些用作我的成功标准:

With an object-oriented abstraction over the data access like EF, I'm trying to imagine how I can get to a place where I don't need to change any of the underlying code outside of the dbcontext to make this work. Essentially, I want to use these as my success criteria:

  • 不必更改现有的数据访问代码.有很多,其中很多是程序性的和重复的.不幸的是,没有存储库类,而且就我想达到的程度而言,这是我必须推迟的技术债务.
  • 查询会过滤租户ID上的那些顶级对象.因此,例如,现有代码获取context.Members.Where(x => x.IsAwesome),但魔术地还可以过滤到租户ID等于租户ID的位置(每个请求都可以使用租户上下文,并且可以将其注入).
  • 添加顶级实体还会分配租户ID.换句话说,代码执行的操作类似于context.Members.Add(newEntity)和newEntity神奇地将其TenantID属性设置为可通过注入的组件使用的ID.

似乎可以通过实体类本身来设置租户ID(没有想到通过注入来实现,某种垫片插在了那里),但是我不确定如何最好地添加用于查询的附加过滤器.

It seems like setting the tenant ID could be done with the entity class itself (haven't thought through the injection on that, some kind of shim stuck in there), but I'm not sure how best to go about adding an additional filter for querying.

推荐答案

我不确定在不更改代码的情况下是否可以完全完成此操作,但是我将按照以下方式进行处理.首先,为您的多租户实体引入一个接口(我假设它们每个都具有TenantID属性,映射到数据库列):

I'm not sure it can be done completely without code changes, but here's how I would approach this. First, introduce an interface for your multi-tenant entities (I assume each of them has TenantID property, mapped to database column):

public interface IMultiTenantEntity {
    int TenantID { get; set; }
}

然后为您的所有实体实施它.它们是自动生成的,但是是部分生成的,因此只需执行以下操作即可:

Then implement it for all your entities. They are autogenerated, but partial, so just do:

public partial class YourEntity : IMultiTenantEntity {}

现在,要在保存时填充此属性,请在您的上下文中覆盖SaveChanges(再次,它是自动生成的,但是是部分生成的,因此您无需触摸自动生成的代码):

Now, to fill this property on saving, override SaveChanges in your context (again, it's autogenerated, but partial, so you don't have to touch autogenerated code):

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字段中.

Above I assume you already injected current tenant id into _tenantId field somehow.

然后,对于每个实体集,添加单独的属性,该属性将返回由TenantID过滤的该集合(再次在您的上下文的局部类中):

Then, for each entity set, add separate property which will return this set filtered by TenantID (again in partial class for your context):

public IQueryable<YourEntity> TenantYourEntities => this.YourEntities.Where(c => c.TenantID == _tenantId);

现在,您所需要做的就是找到对YourEntities集的所有引用(通过右键单击>查找所有引用),并将其替换为对TenantYourEntities的引用.然后,您的所有查询将被TenantID过滤而无需太多工作.当然,不要在使用DbSet修改实体(Db.YourEntities.Add(...))的地方替换引用.

Now all you need to do is find all references to the sets of YourEntities (with right-click > find all references) and replace them with references to TenantYourEntities. Then all your queries will be filtered by TenantID without much work. Of course, don't replace references where you use DbSet to modify entities (Db.YourEntities.Add(...)).

这篇关于通过上下文覆盖将基于EF的应用转换为多租户的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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