2013-04-09 15 views
8

Recentemente ho apportato modifiche alla mia Applicazione MVC3 nel tentativo di smaltire correttamente gli oggetti DbContext [1]. Questo ha funzionato molto in fase di sviluppo, ma una volta che l'applicazione è stata trasferita al mio server di produzione, ho iniziato a intermittenza ottenendo alcune eccezioni divertenti che si sarebbero protratte fino a quando l'AppPool non fosse stato riciclato. Le eccezioni si possono far risalire al codice nel mio personalizzato AuthorizeAttribute e assomigliano:Problemi dopo lo smaltimento 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'. 

(schema di database simile a questa: gli utenti: [Guid, String, ...], i diritti: [Guid, Int32,. ..])

È come se alcuni "fili sono stati incrociati" e l'applicazione sta mescolando i risultati del database: cercando di materializzare il risultato Right come User e viceversa.

Per gestire lo smaltimento di DbContext, ho inserito il codice per archiviarlo a livello di controller. Quando il controller viene smaltito, dispongo anche lo DbContext. So che è hacky, ma lo AuthorizeAttribute utilizza lo stesso contesto tramite filterContext.Controller.

C'è qualcosa di sbagliato nella gestione del ciclo di vita dell'oggetto di DbContext in questo maniero? Ci sono spiegazioni logiche sul motivo per cui sto ricevendo le eccezioni incrociate sopra?

[1] Anche se capisco che non è necessario smaltire gli oggetti DbContext, di recente mi sono imbattuto in diverse fonti che affermavano che si trattava della migliore pratica a prescindere.

Edit (per @ commento di MikeSW)

Una proprietà del AuthorizeAttribute che rappresenta il DbContext è stato impostato nel metodo OnAuthorization, quando il AuthorizationContext è portata. Successivamente, questa proprietà viene utilizzata nel metodo AuthorizeCore.

+0

Puoi condividere parte del codice pertinente della vostra abitudine AuthorizeAttribute? Si noti che un attributo viene utilizzato come singleton da asp.net mvc. Inoltre stai usando un contenitore DI? – MikeSW

+0

@MikeSW Ho aggiunto informazioni sull'utilizzo sopra riportato. Non sto usando un contenitore DI. Con le informazioni fornite sopra, sembrerebbe che questi errori si verifichino a causa della concorrenza: nel tempo che intercorre tra "OnAuthorization" e "AuthorizeCore", un'altra richiesta attiva "OnAuthorization" e clobbers la proprietà 'DbContext'. Questo segue? –

+0

Sì, questo è il tuo problema. [Autorizza] è fondamentalmente un singleton e stai modificando la proprietà dbcontext con ogni richiesta. Io suggerisco di usare un DI Container, registrarsi DbContext con tempo di vita HttpPerInstance quindi utilizzare DependencyResolver.Current.GetService () in OnAuthorization metodo. Il contenitore deve gestire anche lo smaltimento di DbContext – MikeSW

risposta

0

Prima di tutto, vi consiglio di familiarizzare "veramente" con ASP.NET Application Life Cycle Overview for IIS 7.0 in quanto è fondamentale per una buona progettazione dell'applicazione MVC.

Ora per cercare di "imitare" la base di codice

Diciamo che avete un simile MembershipProvider personalizzato come descritto qui https://stackoverflow.com/a/10067020/1241400

allora si avrebbe solo bisogno di un personalizzato Authorize attributo

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

che è possibile utilizzare su qualsiasi controller o azione specifica

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

Se lo desideri, puoi anche iscriverti all'evento PostAuthorizeRequest e scartare i risultati in base ad alcuni criteri.

protected void Application_PostAuthorizeRequest(Object sender, EventArgs e) 
     { 

      //do what you need here 
     } 

Per quanto riguarda il DbContext, non ho mai incontrato la vostra situazione e sì per request è l'approccio giusto in modo da poter disporre nel controller o nel repository.

Ovviamente si consiglia di utilizzare filters e quindi aggiungere l'attributo [AllowAnonymous] alle proprie azioni.

1

si fa realmente necessità di smaltire il contesto?

Secondo this post da Jon Gallant che è stato in contatto con il team Microsoft ADO.NET Entity Framework:

devo sempre chiamare Dispose() su miei oggetti DbContext? No

Prima ho parlato con gli sviluppatori del team di EF la mia risposta è sempre stata un sonoro “naturalmente!”. Ma non è vero con DbContext. Non è necessario essere religiosi riguardo alla chiamata Dispose sugli oggetti DbContext. Anche se implementa IDisposable, lo implementa solo in modo da poter chiamare Dispose come salvaguardia in alcuni casi speciali. Di default DbContext gestisce automaticamente la connessione per te.