[英]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確實非常有效。基本設計是(這里是我的責任...):
WHERE (ClientSid = SUSER_SID())
在表上的直接選擇,但不選擇ClientSid(有效地公開表的接口) 差不多就可以了-盡管分享可能會有用。 我知道這不是您問題的直接答案,但是這導致C#區域中的自定義代碼基本上為零。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.