2013-04-09 18 views
8

Ich habe vor kurzem Änderungen an meiner MVC3-Anwendung in dem Versuch, die DbContext Objekte ordnungsgemäß zu entsorgen [1]. Dies funktionierte großartig in der Entwicklung, aber sobald die Anwendung auf meinen Produktionsserver übertragen wurde, begann ich intermittierend einige lustige Ausnahmen zu bekommen, die so lange bestehen blieben, bis der AppPool recycelt wurde. Die Ausnahmen zurück zum Code in meine Gewohnheit werden kann AuthorizeAttribute verfolgt und wie folgt aussehen:Probleme nach der Bereitstellung von 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'. 

(Datenbankschema sieht wie folgt aus: Benutzer: [Guid, String, ...], Rechte: [Guid, Int32. ..])

Es ist, als ob einige "Drähte werden gekreuzt", und die Anwendung verwechselt Ergebnisse aus der Datenbank: Versuchen, das Right Ergebnis als User und umgekehrt.

Um die Entsorgung von DbContext zu verwalten, legte ich Code, um dies auf einer Controller-Ebene zu speichern. Wenn die Steuerung entsorgt wird, entsorgen wir auch die DbContext. Ich weiß, es ist hacky, aber die AuthorizeAttribute verwendet den gleichen Kontext über filterContext.Controller.

Stimmt etwas nicht mit der Handhabung des Objektlebenszyklus DbContext in diesem Herrenhaus? Gibt es irgendwelche logischen Erklärungen dafür, warum ich die oben genannten Ausnahmen überschreite?

[1] Obwohl ich verstehe, dass es nicht notwendig ist, DbContext Objekte zu entsorgen, stieß ich kürzlich auf eine Anzahl von Quellen, die besagen, dass es unabhängig davon Best Practice war.

Edit (pro @ MikeSW Kommentar)

Eine Eigenschaft der AuthorizeAttribute repräsentiert die DbContext in den OnAuthorization Verfahren eingestellt werden, wenn die AuthorizationContext im Gültigkeitsbereich befindet. Diese Eigenschaft wird später in der AuthorizeCore-Methode verwendet.

+0

Können Sie einige der relevanten Code Ihrer benutzerdefinierten AuthorizeAttribute teilen? Beachten Sie, dass ein Attribut von asp.net mvc als Singleton verwendet wird. Verwenden Sie auch einen DI-Container? – MikeSW

+0

@MikeSW Ich habe Informationen zur obigen Verwendung hinzugefügt. Ich verwende keinen DI-Container. Mit den Informationen, die ich oben angegeben habe, scheint es, als ob diese Fehler aufgrund von Nebenläufigkeit auftreten: In der Zeit zwischen 'OnAuthorization' und 'AuthorizeCore' löst eine andere Anfrage' OnAuthorization' aus und überlagert die 'DbContext'-Eigenschaft. Folgt das? –

+0

Ja, das ist dein Problem. Die [Autorisieren] ist im Grunde ein Singleton und Sie ändern die dbcontext -Eigenschaft bei jeder Anfrage. Ich schlage vor, einen DI-Container zu verwenden, DbContext mit HttpPerInstance-Lebensdauer zu registrieren und dann DependencyResolver.Current.GetService () in OnAuthorization-Methode zu verwenden. Der Container sollte auch die Entsorgung von DbContext übernehmen. – MikeSW

Antwort

0

Zuerst empfehle ich, dass Sie sich "wirklich" mit ASP.NET Application Life Cycle Overview for IIS 7.0 vertraut machen, da es für ein gutes MVC-Anwendungsdesign grundlegend ist.

Jetzt zu versuchen und „nachahmen“ Ihr Code-Basis

Angenommen, Sie haben einen ähnlichen Brauch MembershipProvider haben wie hier beschrieben https://stackoverflow.com/a/10067020/1241400

dann würden Sie nur eine benutzerdefinierte benötigen Authorize Attribut

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); 
    }   
} 

die Sie auf jedem Controller oder einer bestimmten Aktion verwenden können

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

Wenn Sie möchten, können Sie auch das PostAuthorizeRequest-Ereignis abonnieren und die Ergebnisse basierend auf einigen Kriterien verwerfen.

protected void Application_PostAuthorizeRequest(Object sender, EventArgs e) 
     { 

      //do what you need here 
     } 

Was die DbContext, habe ich nie in der Situation kommen und ja per request ist der richtige Ansatz, so dass Sie es in der Steuerung oder in Ihrem Repository verfügen können.

Natürlich wird empfohlen, dass Sie filters verwenden und dann Ihren Aktionen das Attribut [AllowAnonymous] hinzufügen.

1

Müssen Sie tatsächlich den Kontext entsorgen?

Nach this post von Jon Gallant, die in Kontakt mit dem Microsoft ADO.NET Entity Framework-Team war:

Muss ich immer anrufen Dispose() auf meinem DbContext Objekte? Nö

Bevor ich mit den Entwicklern im EF Team gesprochen habe, war meine Antwort immer ein klares "natürlich!". Aber das stimmt nicht mit DbContext. Sie müssen nicht religiös sein, wenn Sie Dispose für Ihre DbContext-Objekte aufrufen. Obwohl es IDisposable implementiert, implementiert es es nur, so dass Sie in einigen Sonderfällen Dispose als eine Sicherheitsmaßnahme aufrufen können. Standardmäßig verwaltet DbContext automatisch die Verbindung für Sie.