Frage

Ich habe für eine Weile NHibernate benutzen jetzt und habe von Zeit zu Zeit festgestellt, dass wenn ich versuche, zwei Seiten zu beantragen simultaniously (oder so nah wie ich kann), es wird gelegentlich Fehler. So ging ich davon aus, dass es war, weil meine Session-Management nicht Thread-sicher war.

Ich dachte, es ist meine Klasse war so habe ich versucht, eine andere Methode aus diesem Blog-Post verwenden http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way/ aber bekomme ich immer noch die gleichen Probleme. Der tatsächliche Fehler Ich erhalte ist:

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

Entweder das, oder kein Datareader ist offen, aber das ist der Hauptschuldige.

Ich habe meine Session-Management-Klasse platziert unten, jeder kann erkennen, warum ich diese Probleme werden mit kann?

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

}

Und die tatsächlichen Speicher ich verwende, sind dies:

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

ich initialisieren die Session auf Application_Start oben mit:

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

Aktualisieren

Hallo Leute, vielen Dank für Ihren Rat, ich habe ein paar verschiedene Dinge ausprobiert, um zu versuchen, den Code zu vereinfachen, aber ich bin mit immer noch in die gleichen Probleme und ich kann eine Idee haben, warum.

Ich schaffe die Sitzung pro Anforderung wie und wann es erforderlich ist, aber in meinem global.asax ich der Sitzung auf Application_EndRequest bin zu entsorgen. Allerdings finden bin ich die Application_EndRequest mehr als einmal gebrannt wird, während ich in debug am Ende bin eine Seite geladen wird. Ich dachte, dass das Ereignis nur einmal am Ende des Antrags zu feuern ist wohl aber, wenn es nicht und einige andere Gegenstände ist versuchen, die Session zu verwenden (das ist, was der Fehler über beschwert) aus irgendeinem seltsamen Grund, das könnte seine mein Problem und die Session noch Thread-sicher ist es nur entsorgt werden zu früh.

got Wer irgendwelche Ideen? Ich habe eine Google und sah, dass der VS Entwicklung Server-Probleme verursacht, wie das, aber ich es durch IIS leite.

War es hilfreich?

Lösung

Während ich Ihre gesamte Code-Basis oder das Problem ein Umdenken zu lösen Sie versuchen, nicht gesehen haben, wie Sie NHibernate verwenden könnte in Ordnung sein. Von der Dokumentation :

  

Sie sollten folgende Punkte beachten   Praktiken bei der Erstellung von NHibernate   Sessions:

     
      
  • Sie nie mehr als eine gleichzeitige erstellen   ISession oder ITransaction Instanz pro   Datenbankverbindung.

  •   
  • sehr vorsichtig sein bei der Erstellung   mehr als eine ISession pro Datenbank   pro Transaktion. Die ISession selbst   Überblick über Updates hält zu loaded gemacht   Objekte, so eine andere ISession könnte   siehe veraltete Daten.

  •   
  • Die ISession ist nicht THREAD! Nie   Zugriff auf die gleiche ISession in zwei   gleichzeitige Threads. Ein ISession ist   ein in der Regel nur Einheit-of-Arbeit

  •   

Das letzte Bit ist das relevanteste (und wichtig im Fall einer Multithread-Umgebung) zu dem, was ich sage. Ein ISession sollte einmal für eine kleine atomare Operation und dann entsorgt werden. Auch aus der Dokumentation:

  

Ein ISessionFactory ist ein   teuer zu erstellen, THREAD Objekt   soll von allen geteilt werden   Anwendungs-Threads. Ein ISession ist ein   preiswert, nicht-Objekt THREAD   das sollte einmal verwendet werden, für ein einzelnes   Geschäftsprozess, und dann verworfen.

diese beiden Ideen kombinierend, anstatt die ISession der Speicherung selbst, speichern Sie die Sitzung Fabrik seit, dass das „große“ Objekt ist. Sie können dann so etwas wie SessionManager.GetSession () als Wrapper verwenden, um die Fabrik aus dem Sitzungsspeicher abzurufen und eine Sitzung instanziiert und es für einen Vorgang zu verwenden.

Das Problem ist auch weniger offensichtlich im Zusammenhang mit einer ASP.NET-Anwendung. Sie Scoping statisch das ISession Objekt, das bedeutet, dass es über die AppDomain geteilt wird. Wenn zwei unterschiedliche Seitenaufrufe innerhalb betreffenden AppDomain Lebenszeit erstellt werden und gleichzeitig ausgeführt werden, haben Sie jetzt zwei Seiten (verschiedene Threads) die gleiche ISession zu berühren, was nicht sicher.

Im Grunde genommen, anstatt zu versuchen, so lange wie möglich eine Sitzung zu halten um, versuchen sie los zu bekommen, so bald wie möglich und sehen, ob Sie bessere Ergebnisse haben.

EDIT:

Ok, ich kann sehen, wo Sie mit diesem zu gehen versuchen. Es klingt wie Sie versuchen, die Open Session im Ansichtsmuster zu implementieren und dort ein paar verschiedenen Routen können Sie sich auf das nehmen:

Wenn Sie einen anderen Rahmen hinzuzufügen, ist kein Problem, schauen Sie in so etwas wie Spring.NET . Es ist modular, damit Sie nicht die ganze Sache zu verwenden, Sie könnten nur das Helfermodul NHibernate verwenden. Es unterstützt die offene Sitzung im Hinblick auf Muster. Dokumentation hier (Rubrik 21.2.10. "Web-Session-Management" ).

Wenn Sie lieber würde Ihre eigene Rolle, lesen Sie in diesem Codeproject Posting von Bill McCafferty: "NHibernate Best Practices" . Gegen Ende beschreibt er das Muster über eine benutzerdefinierte IHttpModule implementieren. Ich habe auch Beiträge rund um das Internet für die Durchführung des Musters ohne IHttpModule gesehen, aber das könnte sein, was Sie schon versucht worden.

Meine üblichen Muster (und vielleicht haben Sie bereits übersprungen hier voraus) zunächst einen Rahmen verwenden. Es beseitigt viele Kopfschmerzen. Wenn es zu langsam ist oder nicht passt meine Bedürfnisse dann versuche ich die Konfiguration zu optimieren oder sie anpassen. Erst danach kann ich versuchen, meine eigene Rolle, aber YMMV. :)

Andere Tipps

Ich kann nicht sicher sein, in NHibernate aber in Hibernate Session-Objekten (wie ich ein Java Hibernate Typ bin) sind nicht sicher durch Design fädeln. Sie sollten eine Sitzung öffnen und schließen und es nie aus dem Rahmen des aktuellen Threads ermöglichen.

Ich bin sicher, dass Muster wie ‚Open Session-Ansicht‘ haben in .Net irgendwo implementiert.

Die andere interessante Frage ist, wenn Sie eine Hibernate-Einheit in der Sitzung gestellt. Das Problem hierbei ist, dass die Sitzung, die es gebunden ist (oder sollte) auf Antrag Fertig geschlossen. Sie haben das Unternehmen an die neue (Hibernate) Sitzung wieder zu befestigen, wenn Sie alle nicht geladenen Verbände navigieren wollen. Dies ist es selbst ein neues Problem verursacht, wenn zwei Anfragen versuchen, dies zur gleichen Zeit zu tun, als etwas Luft zu sprengen, wenn Sie versuchen, eine Einheit zu zwei Sitzungen zu befestigen.

Hoffe, das hilft. Gareth

Das Problem war dann aber, dass meine Bibliothek zur Umkehrung der Steuer war nicht die Objekte, die Verwaltung korrekt in HTTP-Kontext geschaffen wurde, so wurde ich Referenzen für Objekte erhalten, die in diesem Zusammenhang nicht zur Verfügung sollte. Dies war mit Ninject 1.0, sobald ich Ninject aktualisiert 2.0 (beta) das Problem behoben wurde.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top