簡體   English   中英

通過上下文覆蓋將基於 EF 的應用程序轉換為多租戶

[英]Convert EF-based app to multi-tenant by way of context overrides

我有一個實體框架,我必須制作多租戶的基於代碼的應用程序,也就是說,現在大約有六個“頂級”實體需要引用特定的租戶 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 初始化通常不在請求管道內完成。 在這一點上,你可能只需要手動設置的值,或以其他方式創建內代碼的背景下,請求管道,如控制器的一部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM