我有一個多租戶ASP.NET應用程序,我們的數據庫設置了軟刪除。最初,我們直接在查詢級別處理數據限制,例如:
var foos = context.Foos.Where(foo => !foo.Deleted && foo.TenantId = currentTenantId).ToList();
可以想像,這會使我們的數據訪問層中的所有查詢失敗,並且如果忘記添加正確的過濾條件,則會使API非常容易受到攻擊。我們決定使用Z.EntityFramework.Plus.EF6
將全局過濾應用於上下文:
public class FooDataContextFactory
{
public FooDataContext CreateContext()
{
var context = new FooDataContext();
context.Filter<Foo>(collection => collection.Where(foo=> !foo.Deleted));
var principal = Thread.CurrentPrincipal as ClaimsPrincipal;
if (principal.HasClaim(claim => claim.Type == "TenantId"))
{
var currentTenantId = int.Parse(principal.FindFirst("TenantId").Value);
context.Filter<Foo>(collection => collection.Where(foo => foo.TenantId == currentTenantId));
}
return context;
}
}
這適用於單個用戶。但是,當您切換租戶時,我們遇到的問題是過濾器表達式保存在查詢計劃緩存中 。這是Entity Framework Plus的一個已知問題 ,由於它似乎沒有得到解決,我需要找到一種解決方法。
我能想到的最直接的解決方案是將查詢計劃緩存的生命週期與當前會話相關聯,當用戶註銷或切換租戶時,緩存將被銷毀。這是可能的,如果是這樣,我怎樣才能實現這一目標?
我有這個完全相同的問題,並嘗試與Z.EntityFramework.Plus.EF6一起使用相同的問題。我發現zzzprojects團隊還有EntityFramework.DynamicFilters ,它可以更好地實現此目的。緩存的查詢是參數化的,並使用您提供的選擇器函數在運行時注入該值。
using System.Data.Entity;
using EntityFramework.DynamicFilters;
public class Program
{
public class CustomContext : DbContext
{
private int _tenantId;
public int GetTenantId()
{
return _tenantId;
}
// Call this function to set the tenant once authentication is complete.
// Alternatively, you could pass tenantId in when constructing CustomContext if you already know it
// or pass in a function that returns the tenant to the constructor and call it here.
public void SetTenantId(int tenantId)
{
_tenantId = tenantId;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Filter applies to any model that implements ITenantRestrictedObject
modelBuilder.Filter(
"TenantFilter",
(ITenantRestrictedObject t, int tenantId) => t.TenantId == tenantId,
(CustomContext ctx) => ctx.GetTenantId(), // Might could replace this with a property accessor... I haven't tried it
opt => opt.ApplyToChildProperties(false)
);
}
}
public interface ITenantRestrictedObject
{
int TenantId { get; }
}
}