【问题标题】:Problems After Disposing DbContext处理 DbContext 后的问题
【发布时间】:2013-04-09 20:25:04
【问题描述】:

我最近对我的 MVC3 应用程序进行了更改,以尝试正确处理 DbContext 对象 [1]。这在开发中效果很好,但是一旦应用程序被推送到我的生产服务器,我开始间歇性地收到一些有趣的异常,这些异常会一直持续到 AppPool 被回收。异常可以追溯到我自定义的AuthorizeAttribute 中的代码,如下所示:

System.InvalidOperationException: The 'Username' property on 'User' could not be set to a 'Int32' value. You must set this property to a non-null value of type 'String'.

System.InvalidOperationException: The 'Code' property on 'Right' could not be set to a 'String' value. You must set this property to a non-null value of type 'Int32'. 

(数据库架构如下所示:用户:[Guid, String, ...],权限:[Guid, Int32, ...])

就好像一些“线正在交叉”,并且应用程序正在混合来自数据库的结果:试图将Right 结果具体化为User,反之亦然。

为了管理DbContext 的处置,我将代码放入以将其存储在每个控制器级别。当控制器被处置时,我也处置了DbContext。我知道这很hacky,但AuthorizeAttribute 通过filterContext.Controller 使用相同的上下文。

在这个庄园里处理DbContext的对象生命周期有什么问题吗?关于为什么我得到上面的交叉异常有任何合乎逻辑的解释吗?

[1] 虽然我知道没有必要处置 DbContext 对象,但我最近发现一些消息来源表明无论如何这是最佳做法。

编辑(根据@MikeSW 的评论)

AuthorizationContext 在范围内时,在OnAuthorization 方法中设置了代表DbContextAuthorizeAttribute 属性。该属性随后在AuthorizeCore 方法中使用。

【问题讨论】:

  • 你能分享一些你自定义AuthorizeAttribute的相关代码吗?请注意,asp.net mvc 将属性用作单例。你还在使用 DI 容器吗?
  • @MikeSW 我添加了有关上述用法的信息。我没有使用 DI 容器。根据我上面提供的信息,这些错误似乎是由于并发性而发生的:在OnAuthorizationAuthorizeCore 之间的时间里,另一个请求触发了OnAuthorization 并破坏了DbContext 属性。是这样吗?
  • 是的,这是你的问题。 [Authorize] 基本上是一个单例,您在每次请求时更改 dbcontext 属性。我建议使用 DI 容器,使用 HttpPerInstance 生命周期注册 DbContext,然后在 OnAuthorization 方法中使用 DependencyResolver.Current.GetService()。容器也应该处理 DbContext 的处置
  • @MikeSW 我们正在考虑添加一个 DI 容器,但正在寻找替代解决方案。在OnAuthorization 内部的this 上使用同步锁会产生任何问题,至少可以在短期内缓解问题吗?只是在我们当前的应用规模下,添加一个 DI Container 并不是一件小事。
  • 不,我会使用 DI 容器。这是最简单、最干净的解决方案。 Asp.Net mvc 知道如何使用它,你真的只能用它来解决这个问题。

标签: asp.net-mvc-3 entity-framework-4 dispose dbcontext


【解决方案1】:

你真的需要处理上下文吗?

据与 Microsoft ADO.NET 实体框架团队有联系的 Jon Gallant 的this post

我是否总是必须在我的 DbContext 对象上调用 Dispose()?没有

在与 EF 团队的开发人员交谈之前,我的回答总是响亮的“当然!”。但 DbContext 并非如此。您无需对在 DbContext 对象上调用 Dispose 感到虔诚。尽管它确实实现了 IDisposable,但它只是实现了它,因此您可以在某些特殊情况下调用 Dispose 作为保护措施。默认情况下,DbContext 会自动为您管理连接。

【讨论】:

    【解决方案2】:

    首先,我建议您“真正”熟悉 ASP.NET Application Life Cycle Overview for IIS 7.0,因为它是良好 MVC 应用程序设计的基础。

    现在尝试“模仿”您的代码库

    假设您有一个类似的自定义 MembershipProvider,如此处所述https://stackoverflow.com/a/10067020/1241400

    那么你只需要一个自定义的Authorize 属性

    public sealed class AuthorizeByRoles : AuthorizeAttribute
        {
            public AuthorizeByRoles(params UserRoles[] userRoles)
            {
                this.Roles = AuthorizationHelper.GetRolesForEnums(userRoles);
            }
        }
    
    public static class AuthorizationHelper
    {       
        public static string GetRolesForEnums(params UserRoles[] userRoles)
        {
            List<string> roles = new List<string>();
            foreach (UserRoles userRole in userRoles)
            {
                roles.Add(GetEnumName(userRole));
            }
            return string.Join(",", roles);
        }
    
        private static string GetEnumName(UserRoles userRole)
        {
            return Enum.GetName(userRole.GetType(), userRole);
        }        
    }
    

    您可以在任何控制器或特定操作上使用它

     [AuthorizeByRoles(UserRoles.Admin, UserRoles.Developer)]
     public class MySecureController : Controller
     {
          //your code here
     }
    

    如果您愿意,您也可以订阅PostAuthorizeRequest 事件并根据某些条件丢弃结果。

     protected void Application_PostAuthorizeRequest(Object sender, EventArgs e)
            {
    
                //do what you need here
            }
    

    至于DbContext,我从来没有遇到过你的情况,是的,per request 是正确的方法,所以你可以在控制器或存储库中处理它。

    当然建议您使用filters,然后将 [AllowAnonymous] 属性添加到您的操作中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-29
      相关资源
      最近更新 更多