我需要根据用户的权限动态过滤特定表中的数据。例如,“普通”用户只能看到分配给他的记录,但管理员可以看到全部。我正在使用 ninject 为每个请求创建数据库上下文,并通过将其他用户信息传递给构造函数来创建上下文。然后我从 EntityFramework-Plus 扩展应用动态过滤(EF6):
public MyDbContext(bool isAdmin, string userId) : this()
{
if (!isAdmin)
{
this.Filter<MyTable>(table => table.Where(...));
}
}
该解决方案按预期工作,即调用如下方法:
ctx.MyTable.Where(...)
导致过滤器中声明的额外连接。
但是当我使用方法 Find() 时行为很奇怪。我正在使用 SqlServer 探查器来查看幕后发生的事情:
- 我将上下文创建为受限(非管理员用户) - 调用 Find() 将导致额外的 WHERE 语句对应于过滤器的 lambda 表达式
- 然后我以管理员身份创建上下文(单独的请求)——调用 Find() 将产生相同的 SQL 表达式(我希望没有额外的 SQL 子句)。
AFAIK 这与查询缓存有关,因为向构造函数添加额外的行似乎可以解决问题:
public MyDbContext(bool isAdmin, string userId) : this()
{
// this "solves" the problem
QueryFilterManager.ClearQueryCache(this);
if (!isAdmin)
{
this.Filter<MyTable>(table => table.Where(...));
}
}
这看起来像是一个很大的矫枉过正,它并没有让我更接近于理解这个问题。所以这是我的问题:
- 为什么这个问题不影响 Where() 而是影响 Find()?
- 有没有更清洁的方法来解决这个问题?我已经阅读了有关动态过滤器库的信息,但这对我没有好处,因为它仅适用于代码优先模型(此处为 DB 优先)。
- 是否有更好的基于每个请求数据过滤数据的概念(例如我的示例中的 userId)?
更新
这就是我的 lambda 表达式的样子:
private Func<IQueryable<MyTable>, IQueryable<MyTable>> GetFilter(string userId)
{
return t => t
.Where(c.DataScopes.Any(
x => x.AspNetGroups.Any(
ang => ang.AspNetUsers.Any(
anu => anu.Id == userId))));
}
AspNetGroups 是我用于对用户进行分组的自定义表。数据权限分配给用户组。