質問

私はしばらくNHibernateを使用してきましたが、2ページを同時に(またはできるだけ近くに)要求しようとすると、時々エラーになることがあります。それで、セッション管理がスレッドセーフではなかったためだと思いました。

自分のクラスだと思ったので、このブログ投稿とは異なる方法を使用しようとしました http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way/ しかし、私はまだ同じ問題を抱えています。私が得ている実際のエラーは次のとおりです:

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

データリーダーが開いているか開いていないかのどちらかですが、これが主な原因です。

セッション管理クラスを下に配置しましたが、これらの問題が発生している理由を誰かが見つけられますか?

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

}

そして実際に使用しているストアは次のとおりです。

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

Application_Start upでSessionFactoryを初期化します:

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

更新

皆さん、アドバイスありがとう、コードを単純化するためにいくつかの異なることを試してみましたが、私はまだ同じ問題に直面しており、理由がわかるかもしれません。

リクエストごとに必要なときにセッションを作成しますが、global.asaxではApplication_EndRequestでセッションを破棄しています。ただし、ページの読み込みの最後にデバッグ中にApplication_EndRequestが複数回起動されていることがわかりました。イベントはリクエストの最後に一度だけ発生するはずだと思っていましたが、そうではなく、他のいくつかのアイテムがセッションを使用しようとしている場合(エラーが不平を言っています)私の問題であり、セッションはまだスレッドセーフであり、早期に廃棄されています。

アイデアはありますか?私はグーグルをし、VS開発サーバーがそのような問題を引き起こすことがわかりましたが、IISを介して実行しています。

役に立ちましたか?

解決

コードベース全体や解決しようとしている問題を見たことはありませんが、NHibernateの使用方法を再考することが適切かもしれません。 ドキュメントから:

  

次のことに注意してください   NHibernateを作成するときのプラクティス   セッション:

     
      
  • 複数のコンカレントを作成しない   ISessionまたはITransactionインスタンスごと   データベース接続。

  •   
  • 作成するときは非常に注意してください   データベースごとに複数のISession   トランザクションごと。 ISession自体   ロードされた更新を追跡します   オブジェクト。したがって、異なるISessionは   古いデータを参照してください。

  •   
  • ISessionはスレッドセーフではありません   2つの同じISessionにアクセスする   同時スレッド。 ISessionは   通常、単一の作業単位のみです!

  •   

この最後の部分は、私が言っていることと最も関連性があります(マルチスレッド環境の場合は重要です)。 ISessionは、小さなアトミック操作に一度使用してから破棄する必要があります。また、ドキュメントから:

  

ISessionFactoryは   作成コストが高く、スレッドセーフなオブジェクト   すべてによって共有されることを意図   アプリケーションスレッド。 ISessionは   安価でスレッドセーフでないオブジェクト   一度だけ使用する必要があります   ビジネスプロセス、そして破棄されます。

これら2つのアイデアを組み合わせて、ISession自体を保存する代わりに、セッションファクトリを保存します。オブジェクト。次に、SessionManager.GetSession()のようなものをラッパーとして使用して、セッションストアからファクトリを取得し、セッションをインスタンス化して、1つの操作に使用できます。

この問題は、ASP.NETアプリケーションのコンテキストでもそれほど明確ではありません。 ISessionオブジェクトを静的にスコープしているため、AppDomain全体で共有されています。 AppDomainの有効期間内に2つの異なるページリクエストが作成され、同時に実行された場合、2つのページ(異なるスレッド)が同じISessionにアクセスすることになります。これは安全ではありません。

基本的には、セッションをできるだけ長く維持する代わりに、できるだけ早くそれらを削除し、より良い結果が得られるかどうかを確認してください。

編集:

わかりました、これでどこに行こうとしているのかわかります。 Open Session In Viewパターンを実装しようとしているように思えますが、それにはいくつかのルートがあります:

別のフレームワークを追加しても問題ない場合は、 Spring.NET のようなものを調べてください。モジュール式であるため、すべてを使用する必要はなく、NHibernateヘルパーモジュールを使用できます。ビューパターンでのオープンセッションをサポートします。ドキュメントこちら(見出し21.2.10。" Webセッション管理&quot ;)。

自分で作成したい場合は、Bill McCaffertyによるこのcodeprojectの投稿をご覧ください: " NHibernateベストプラクティス" 。最後に、彼はカスタムIHttpModuleを介してパターンを実装することを説明します。 IHttpModuleを使用せずにパターンを実装するためのインターネット上の投稿も見ましたが、それはあなたが試みてきたものかもしれません。

通常のパターン(そして、ここで既にスキップしているかもしれません)は、最初にフレームワークを使用します。それは多くの頭痛を取り除きます。遅すぎるか、ニーズに合わない場合は、構成を微調整するかカスタマイズします。その後、私は自分自身をロールバックしようとしますが、YMMVです。 :)

他のヒント

NHibernateでは(Java Hibernateの男なので)確信が持てませんが、hibernateではSessionオブジェクトは設計上スレッドセーフではありません。セッションを開いたり閉じたりして、現在のスレッドのスコープ外に許可しないでください。

「セッションビューを開く」などのパターンが.Netのどこかに実装されていると確信しています。

他の興味深い問題は、セッションに休止状態のエンティティを配置する場合です。ここでの問題は、それが接続されているセッションが、リクエストの終了時に閉じられる(または閉じられるべき)ということです。ロードされていない関連付けをナビゲートする場合は、エンティティを新しい(休止状態)セッションに再接続する必要があります。エンティティを2つのセッションにアタッチしようとすると何かが爆発するので、2つの要求が同時にこれを行おうとすると、これは自己の新しい問題を引き起こします。

これが役立つことを願っています。 ガレス

問題は、制御の反転用のライブラリがHTTPコンテキストで作成されたオブジェクトを正しく管理していなかったため、そのコンテキストで使用できないオブジェクトの参照を取得していたことです。これはNinject 1.0を使用していたため、Ninject 2.0(ベータ版)に更新すると問題は解決しました。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top