2013-04-09 100 views
8

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

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,字符串,...],权利:[的Guid,的Int32。 ..])

这就好像有些“电线越过了”,应用程序正在混合来自数据库的结果:试图将Right结果实现为User,反之亦然。

为了管理DbContext的处置,我把代码存储在每个控制器级别。处置控制器时,我也处理DbContext。我知道这很无礼,但AuthorizeAttribute通过filterContext.Controller使用相同的上下文。

在这个庄园里处理DbContext的对象生命周期有什么问题吗?是否有任何合理的解释为什么我得到上面的交叉例外?

虽然我明白没有必要处理DbContext对象,但我最近遇到了很多来源,声称这是最佳实践。

编辑(每@ MikeSW的评论)

表示DbContextOnAuthorization方法被设置,当AuthorizationContext是在范围AuthorizeAttribute的一个性质。此属性随后将在AuthorizeCore方法中使用。

+0

你能分享一下你的自定义AuthorizeAttribute的相关代码吗?请注意,一个属性被asp.net mvc用作单例。你还使用DI容器吗? – MikeSW 2013-04-10 12:25:35

+0

@MikeSW我在上面添加了关于使用的信息。我没有使用DI容器。使用上面提供的信息,看起来好像这些错误是由于并发发生的:在OnAuthorization和AuthorizeCore之间的时间内,另一个请求触发OnAuthorization并且破坏DbContext属性。这是否遵循? – 2013-04-10 15:07:45

+0

是的,那是你的问题。 [Authorize]基本上是一个单例,并且您正在使用每个请求更改dbcontext属性。我建议使用DI容器,用HttpPerInstance生存期注册DbContext,然后在OnAuthorization方法中使用DependencyResolver.Current.GetService ()。容器也应该处理DbContext的处理 – MikeSW 2013-04-10 15:24:18

回答

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]属性。

1

您是否确实需要处理上下文?

根据this post由Jon浩谁一直在接触与Microsoft ADO.NET实体框架团队:

难道我一直都在我的DbContext对象调用Dispose()? Nope

在我与EF团队的开发者交谈之前,我的回答总是“当然!”。但是DbContext并不是这样。你不需要在你的DbContext对象上调用Dispose就会很虔诚。即使它确实实现了IDisposable,但它仅实现它,所以您可以在某些特殊情况下调用Dispose作为安全措施。默认情况下,DbContext自动为您管理连接。