【问题标题】:Where to set DBContext static property for an Entity Framework global filter?在哪里为实体框架全局过滤器设置 DBContext 静态属性?
【发布时间】:2015-09-09 00:20:30
【问题描述】:

我正在尝试构建一个多租户 MVC Web 应用程序,目前进展顺利。在我的应用程序中,我扩展了 asp.net Identity 2 User 属性以包含一个 OrganisationId,其中创建的每个用户都分配给一个组织。即 1 个组织可以有许多与之关联的用户。数据库可以有许多组织。

这是我之前的一篇文章,展示了我如何extended asp.net identity and retrieve the OrganisationId of the logged in user

为了实现实体框架的全局过滤,让登录的用户只能看到他们关联的组织数据,我使用的是github上的Entity Framework Dynamic Filter

我已经设法根据 github 提供的示例代码使全局过滤器工作,我在其中创建了一个静态属性 DBContext 可以根据登录的用户动态更新。

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    //  A static/globally scoped value that will be used to restrict queries by their OrganisationId.
    public static long CurrentUserOrganisationID { get; set; }

    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        ....

        //Filters
        modelBuilder.Filter("Employees_CurrentOrganisation", (Employee e) => e.OrganizationId, () => CurrentUserOrganisationID);
    }
}

我面临的问题是我应该在我的 MCV 应用程序的哪个位置设置/刷新 DBContext 中的当前用户 OrganisationId 静态属性?我可以在登录过程中设置它,但是如果用户在没有单击“注销”的情况下退出应用程序,应用程序将记住他们下次浏览应用程序时已登录并且不执行登录控制器方法。

设置静态OrganisationId属性需要执行的代码是:

ApplicationDbContext.CurrentUserOrganisationID = Convert.ToInt64(HttpContext.User.Identity.GetOrganizationId());

对于这篇文章的标题,我深表歉意,我不确定提出问题的最佳术语! (新手提醒)

【问题讨论】:

  • '我在 DBContext 中创建了一个静态属性,该属性可以根据登录的用户动态更新' - 如果两个用户在同一时间?
  • 无论如何,静态都是静态的。每个 AppDomain 将拥有一个 ApplicationDbContext.CurrentUserOrganisationID,而不是每个请求或用户。为什么不只使用声明?
  • 您好,感谢您的回复。我想在我的 DBContext 中使用静态属性不是一个好主意。运行测试后,我可以确认您是正确的。我真傻!我只是按照动态过滤器的可下载 github 示例中所示的示例进行操作。不知道我应该如何在登录时根据用户设置过滤器。当用户登录时会创建一个声明来存储声明,我只需要一种方法来为登录的各个用户设置过滤器。

标签: asp.net-mvc entity-framework asp.net-identity-2


【解决方案1】:

我不会使用静态属性来定义用户的OrganisationId。静态属性在应用程序中往往是全局的,如果您的 DbContext 生命范围不正确,则多个用户的OrganisationId 将不正确。

如果您使用 DI 框架,我会注入 IPrincipal 并从声明中获取 OrganisationId,然后将其用于过滤。

或者,如果您不介意将 DB 代码与 Web 代码耦合,您可以这样做:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    ....

    //Filters
    modelBuilder.Filter("Employees_CurrentOrganisation", (Employee e) => e.OrganizationId, () => Convert.ToInt64(HttpContext.User.Identity.GetOrganizationId()));
}

但是这种方法增加了在 Web 和 DB 之间添加依赖关系的负面影响。我更喜欢通过构造函数将用户对象注入 DBContext 并在注入用户的数据上定义过滤器。

【讨论】:

  • 谢谢@trailmax 我将研究将用户对象注入 DBContext。不能说我对这种方法了解很多,但我会围绕它进行一些谷歌搜索。我目前使用我的 OrganisationId 声明,如我上面链接到的帖子之一所示。我使用了静态属性,因为它是github example for the EF Dynamic Filter 中详述的方法之一。
  • 代码创建者的评论。 过滤器将 BlogEntries 上的所有查询限制为仅针对当前用户的查询。全局过滤器在这里与委托一起使用,以便在每次需要时对其进行评估。重要提示:确保委托/Func 是否用于在您的应用程序中具有 GLOBAL 范围的值。在此示例中,CurrentAccountID 是静态的。如果它不是静态的,则设置 CurrentAccountID 将无效,因为 Func 将使用 DbContext 上的实例,该实例用于调用 OnModelCreating - 每个应用程序生命周期仅发生一次
  • 很抱歉添加另一个评论,但是如果我将我的数据库代码与 Web 代码结合起来,如果用户没有登录,不会抛出错误,因此 OrganisationId 将为空?
  • @RobHurd 如果用户未经身份验证,他们是否应该达到可以从您的数据库中读取数据的程度?无论如何,您正在使用委托 - 检查用户是否未经身份验证并返回 null 或适合过滤器的内容。
  • 关于作者的评论 - OnModelCreating 每次创建 DbContext 的新实例时都会发生,并且在 Web 应用程序中应该发生在每个 http 请求上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多