处理DbContext后出现问题

时间:2013-04-09 20:25:05

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

我最近对我的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。我知道这很麻烦,但AuthorizeAttribute通过filterContext.Controller使用相同的上下文。

在这个庄园中处理DbContext的对象生命周期有什么问题吗?有没有任何合理的解释,为什么我得到上面的纵横交错的例外?

[1]虽然我知道没有必要处理DbContext个对象,但我最近遇到了一些消息来源,说明这是最好的做法。

编辑(根据@ MikeSW的评论)

AuthorizeAttribute在范围内时,DbContext方法中正在设置代表OnAuthorization的{​​{1}}的属性。然后,此属性将在AuthorizationContext方法中使用。

2 个答案:

答案 0 :(得分:1)

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

Jon Gallant的this post表示,他与Microsoft ADO.NET实体框架团队保持联系:

  

我是否总是在DbContext对象上调用Dispose()?都能跟得上

     

在我与EF团队的开发者交谈之前,我的答案总是响亮的“当然!”。但是DbContext并不是这样。您不需要在DbContext对象上调用Dispose时具有宗教信仰。尽管它确实实现了IDisposable,但它只实现了它,所以你可以在某些特殊情况下调用Dispose作为安全措施。默认情况下,DbContext会自动为您管理连接。

答案 1 :(得分:0)

首先,我建议您“真正”熟悉 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]属性。