2

我需要根据用户的权限动态过滤特定表中的数据。例如,“普通”用户只能看到分配给他的记录,但管理员可以看到全部。我正在使用 ninject 为每个请求创建数据库上下文,并通过将其他用户信息传递给构造函数来创建上下文。然后我从 EntityFramework-Plus 扩展应用动态过滤(EF6):

public MyDbContext(bool isAdmin, string userId) : this()
{
    if (!isAdmin)
    {
        this.Filter<MyTable>(table => table.Where(...));
    }
}

该解决方案按预期工作,即调用如下方法:

ctx.MyTable.Where(...)

导致过滤器中声明的额外连接。

但是当我使用方法 Find() 时行为很奇怪。我正在使用 SqlServer 探查器来查看幕后发生的事情:

  1. 我将上下文创建为受限(非管理员用户) - 调用 Find() 将导致额外的 WHERE 语句对应于过滤器的 lambda 表达式
  2. 然后我以管理员身份创建上下文(单独的请求)——调用 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(...));
    }
}

这看起来像是一个很大的矫枉过正,它并没有让我更接近于理解这个问题。所以这是我的问题:

  1. 为什么这个问题不影响 Where() 而是影响 Find()?
  2. 有没有更清洁的方法来解决这个问题?我已经阅读了有关动态过滤器库的信息,但这对我没有好处,因为它仅适用于代码优先模型(此处为 DB 优先)。
  3. 是否有更好的基于每个请求数据过滤数据的概念(例如我的示例中的 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 是我用于对用户进行分组的自定义表。数据权限分配给用户组。

4

0 回答 0