2013-04-09 83 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自動爲您管理連接。