Domanda

Sto usando NHibernate da un po 'di tempo e ho scoperto di tanto in tanto che se provo a richiedere due pagine simultaneamente (o il più vicino possibile) a volte sbaglierà. Quindi ho pensato che fosse perché la mia gestione della sessione non era sicura per i thread.

Pensavo fosse la mia classe, quindi ho cercato di utilizzare un metodo diverso da questo post del blog http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way/ comunque ho ancora gli stessi problemi. L'errore reale che sto riscontrando è:

Server Error in '/AvvioCMS' Application.
failed to lazily initialize a collection, no session or session was closed
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: NHibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed

O questo o nessun datareader è aperto, ma questo è il principale colpevole.

Ho inserito la mia classe di gestione delle sessioni di seguito, qualcuno può capire perché potrei avere questi problemi?

public interface IUnitOfWorkDataStore
{
    object this[string key] { get; set; }
}


    public static Configuration Init(IUnitOfWorkDataStore storage, Assembly[] assemblies)
    {
        if (storage == null)
            throw new Exception("storage mechanism was null but must be provided");

        Configuration cfg = ConfigureNHibernate(string.Empty);
        foreach (Assembly assembly in assemblies)
        {
            cfg.AddMappingsFromAssembly(assembly);
        }

        SessionFactory = cfg.BuildSessionFactory();
        ContextDataStore = storage;

        return cfg;
    }

    public static ISessionFactory SessionFactory { get; set; }
    public static ISession StoredSession
    {
        get
        {
            return (ISession)ContextDataStore[NHibernateSession.CDS_NHibernateSession];
        }
        set
        {
            ContextDataStore[NHibernateSession.CDS_NHibernateSession] = value;
        }
    }

    public const string CDS_NHibernateSession = "NHibernateSession";
    public const string CDS_IDbConnection = "IDbConnection";

    public static IUnitOfWorkDataStore ContextDataStore { get; set; }

    private static object locker = new object();
    public static ISession Current 
    {
        get 
        {
            ISession session = StoredSession;

            if (session == null) 
            {
                lock (locker)
                {
                    if (DBConnection != null)
                        session = SessionFactory.OpenSession(DBConnection);
                    else
                        session = SessionFactory.OpenSession();

                    StoredSession = session;
                }
            }

            return session;
        }
        set
        {
            StoredSession = value;
        }
    }

    public static IDbConnection DBConnection
    {
        get
        {
            return (IDbConnection)ContextDataStore[NHibernateSession.CDS_IDbConnection];
        }
        set
        {
            ContextDataStore[NHibernateSession.CDS_IDbConnection] = value;
        }
    }

}

E il negozio reale che sto usando è questo:

public class HttpContextDataStore : IUnitOfWorkDataStore
{
    public object this[string key]
    {
        get { return HttpContext.Current.Items[key]; }
        set { HttpContext.Current.Items[key] = value; }
    }
}

Inizializzo SessionFactory su Application_Start con:

NHibernateSession.Init(new HttpContextDataStore(), new Assembly[] { 
                typeof(MappedClass).Assembly});

Aggiorna

Ciao ragazzi, grazie per il vostro consiglio, ho provato alcune cose diverse per provare a semplificare il codice ma sto ancora incontrando gli stessi problemi e potrei avere un'idea del perché.

Creo la sessione per richiesta come e quando è necessario ma nel mio global.asax sto eliminando la sessione su Application_EndRequest. Tuttavia sto riscontrando che Application_EndRequest viene avviato più di una volta mentre sono in debug al termine del caricamento di una pagina. Ho pensato che l'evento dovrebbe essere lanciato solo una volta alla fine della richiesta, ma se non lo è e alcuni altri elementi stanno cercando di utilizzare la Sessione (che è ciò di cui si lamenta l'errore) per qualunque strano motivo che potrebbe essere il mio problema e la sessione è ancora sicura per i thread, è stata eliminata troppo presto.

Qualcuno ha qualche idea? Ho fatto un google e ho visto che il server di sviluppo VS causa problemi del genere ma lo sto eseguendo tramite IIS.

È stato utile?

Soluzione

Anche se non ho visto l'intera base di codice o il problema che stai cercando di risolvere, un ripensamento di come stai usando NHibernate potrebbe essere in ordine. Dalla documentazione :

  

Dovresti osservare quanto segue   pratiche durante la creazione di NHibernate   Sessioni:

     
      
  • Non creare mai più di una concorrente   Istanza ISession o ITransaction per   connessione al database.

  •   
  • Prestare estrema attenzione durante la creazione   più di una ISession per database   per transazione. La stessa ISession   tiene traccia degli aggiornamenti apportati al caricamento   oggetti, quindi una diversa ISession potrebbe   vedere dati non aggiornati.

  •   
  • ISession non è non thread sicuro! Mai   accedere alla stessa ISession in due   discussioni simultanee. Una ISession è   di solito solo una singola unità di lavoro !

  •   

L'ultimo bit è il più rilevante (e importante nel caso di un ambiente multithread) per quello che sto dicendo. Una ISession dovrebbe essere usata una volta per una piccola operazione atomica e poi eliminata. Anche dalla documentazione:

  

Un ISessionFactory è un   oggetto thread-safe costoso da creare   destinato a essere condiviso da tutti   thread dell'applicazione. Una ISession è una   oggetto economico, non sicuro per i thread   che dovrebbe essere usato una volta, per un singolo   processo aziendale e quindi scartato.

Combinando queste due idee, invece di memorizzare la stessa ISession, memorizzare la factory di sessione poiché quella è la "grande" quotazione oggetto. È quindi possibile utilizzare qualcosa come SessionManager.GetSession () come wrapper per recuperare la factory dall'archivio sessioni e creare un'istanza di una sessione e utilizzarla per un'operazione.

Il problema è anche meno evidente nel contesto di un'applicazione ASP.NET. Stai mirando staticamente all'oggetto ISession, il che significa che è condiviso su AppDomain. Se due richieste di pagine diverse vengono create nella vita di quell'AppDomain e vengono eseguite contemporaneamente, ora hai due pagine (thread diversi) che toccano la stessa ISession che è non sicura.

Fondamentalmente, invece di cercare di mantenere una sessione il più a lungo possibile, prova a sbarazzartene il più presto possibile e vedi se hai risultati migliori.

EDIT:

Ok, posso vedere dove stai cercando di andare con questo. Sembra che tu stia cercando di implementare il modello Open Session In View e ci sono un paio di percorsi diversi che puoi seguire su questo:

Se l'aggiunta di un altro framework non è un problema, esaminare qualcosa come Spring.NET . È modulare, quindi non è necessario utilizzare il tutto, è possibile utilizzare semplicemente il modulo di supporto NHibernate. Supporta la sessione aperta nel modello di visualizzazione. Documentazione qui (rubrica 21.2.10. & Quot; Gestione sessioni Web " ;).

Se preferisci realizzare il tuo, dai un'occhiata a questo post di codeproject di Bill McCafferty: " Best practice di NHibernate " . Verso la fine descrive l'implementazione del modello attraverso un IHttpModule personalizzato. Ho anche visto post su Internet per l'implementazione del modello senza un IHttpModule, ma potrebbe essere quello che hai provato.

Il mio solito schema (e forse hai già saltato qui) è usare prima un framework. Rimuove molti mal di testa. Se è troppo lento o non si adatta alle mie esigenze, allora provo a modificare la configurazione o a personalizzarla. Solo dopo provo a fare il mio, ma YMMV. :)

Altri suggerimenti

Non posso esserne certo (dato che sono un tipo di ibernazione Java) in NHibernate, ma in ibernazione gli oggetti Session non sono sicuri per la progettazione. È necessario aprire e chiudere una sessione e non consentirla mai al di fuori dell'ambito del thread corrente.

Sono sicuro che modelli come 'Apri sessione' sono stati implementati in .Net da qualche parte.

L'altro problema interessante è quando metti un'entità in letargo nella sessione. Il problema qui è che la sessione a cui è collegata verrà chiusa (o dovrebbe essere) al termine della richiesta. È necessario ricollegare l'entità alla nuova sessione (ibernazione) se si desidera navigare in associazioni non caricate. Questo di per sé causa un nuovo problema se due richieste provano a farlo contemporaneamente mentre qualcosa esploderà se provi a collegare un'entità a due sessioni.

Spero che questo aiuti. Gareth

Il problema è stato che la mia libreria per l'inversione del controllo non gestiva correttamente gli oggetti creati nel contesto HTTP, quindi stavo ottenendo riferimenti per oggetti che non avrebbero dovuto essere disponibili in quel contesto. Stava usando Ninject 1.0, una volta aggiornato a Ninject 2.0 (beta) il problema è stato risolto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top