我有一个多租户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; }
}
}