簡體   English   中英

虛擬導航屬性和多租戶

[英]Virtual Navigation Properties and Multi-Tenancy

我有一個標准的DbContext ,其代碼如下所示:

 public DbSet<Interest> Interests { get; set; }
 public DbSet<User> Users { get; set; }

我最近通過創建一個包含以下內容的TenantContext實現了多租戶:

  private readonly DbContext _dbContext;
  private readonly Tenant _tenant;

  public TenantContext(Tenant tenant)
        : base("name=DefaultConnection") {
        this._tenant = tenant;
        this._dbContext = new DbContext();
    }

   public IQueryable<User> Users { get { return FilterTenant(_dbContext.Users); } }
   public IQueryable<Interest> Interests { get { return FilterTenant(_dbContext.Interests); } }


   private IQueryable<T> FilterTenant<T>(IQueryable<T> values) where T : class, ITenantData
    {
        return values.Where(x => x.TenantId == _tenant.TenantId);
    }

到目前為止,這一直很好。 每當我的任何服務創建新的TenantContext時,都會直接通過該FilterTenant方法過濾該上下文之外的所有查詢,以確保我僅返回與租戶相關的實體。

我遇到的問題是我對導航屬性的使用沒有考慮到這一點:

  using (var db = CreateContext())  // new TenantContext
        {
            return db.Users.
                Include(u => u.Interests).FirstOrDefault(s => s.UserId == userId);
        }

此查詢拉起特定於租戶的Users ,但是Include()語句僅拉動該用戶的Interests -跨所有租戶。 因此,如果一個用戶在多個租戶中都擁有興趣,則可以通過上述查詢獲得該用戶的所有興趣。

我的用戶模型具有以下內容:

 public int UserId { get; set; }
 public int TenantId { get; set; }
 public virtual ICollection<Interest> Interests { get; set; }

有什么方法可以修改這些導航屬性以執行特定於租戶的查詢? 還是我應該去掉所有導航屬性以支持手寫代碼?

第二個選項使我感到恐懼,因為很多查詢都嵌套了“包含”。 這里的任何輸入都是很棒的。

據我所知,沒有其他方法可以使用反射或手動查詢屬性。

因此,在您的IQueryable<T> FilterTenant<T>(IQueryable<T> values)方法中,您必須檢查T類型的實現ITenantData接口的屬性。

然后您仍然不在那里,因為您的根實體(本例中為User )的屬性可能是實體本身,或者是實體列表(請考慮Invoice.InvoiceLines[].Item.Categories[] )。

對於執行此操作發現的每個屬性,都必須編寫一個Where()子句以過濾這些屬性

或者,您可以按屬性手動對其進行編碼

這些檢查至少應在創建和編輯實體時進行。 您需要檢查由ID屬性引用的導航屬性(例如ContactModel.AddressID ),該屬性已發布到您的存儲庫(例如,從MVC站點),對於當前登錄的租戶是否可訪問。 這是您的批量分配保護,可確保惡意用戶無法發布請求,否則該請求僅通過隨機發布一個隨機請求即可將其擁有權限(他正在創建或編輯的Contact的實體鏈接到另一租戶的一個Address或已知的AddressID

如果您信任此系統,則在讀取時只需檢查根實體的TenantID,因為在創建和更新時進行了檢查,如果可以訪問根實體,則所有子實體都可供租戶訪問。

由於您的描述,您確實需要過濾子實體。 使用此處介紹的技術對示例進行手動編碼的示例:

public class UserRepository
{
    // ctor injects _dbContext and _tenantId

    public IQueryable<User> GetUsers()
    { 
        var user = _dbContext.Users.Where(u => u.TenantId == _tenantId)
                                   .Select(u => new User
                                   {
                                       Interests = u.Interests.Where(u => 
                                                     u.TenantId == _tenantId),
                                       Other = u.Other,
                                   };                               
        }
    }
}

但是正如您所看到的,您必須像這樣映射User每個屬性。

只是想提供一種實現多租戶的替代方法,該方法在當前項目中使用EF5和SQL 2012確實非常有效。基本設計是(這里是我的責任...):

  1. 數據庫中的每個表都有一列(ClientSid二進制,默認約束= SUSER_SID()),並且從不直接查詢,只有通過專用視圖才能查詢
  2. 每個視圖都是使用WHERE (ClientSid = SUSER_SID())在表上的直接選擇,但不選擇ClientSid(有效地公開表的接口)
  3. EF5模型映射到VIEW,而不是TABLE
  4. 連接字符串根據租戶的上下文(用戶/客戶端,無論多租戶分區要求是什么)而變化。

差不多就可以了-盡管分享可能會有用。 我知道這不是您問題的直接答案,但是這導致C#區域中的自定義代碼基本上為零。

暫無
暫無

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

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