3

ORM として NHibernate を使用して C# MVC 3 を作成しましたが、ほとんどのページの読み込みで奇妙な例外がスローされます。それらは主にクローズドセッションなどに関連しているようで、一般的な問題のほとんどを確認しましたが、ほとんど役に立ちませんでした. 例外には次のようなものがあります。

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
   at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.Data.SqlClient.SqlException (0x80131904): The server failed to resume the transaction. Desc:3b00000006.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.NullReferenceException: Object reference not set to an instance of an object.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.Data.SqlClient.SqlException (0x80131904): The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: The transaction is either not associated with the current connection or has been completed.
       at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.InvalidOperationException: SqlConnection does not support parallel transactions.

例外の壁についてお詫び申し上げます。それらは関連していると思われますが、コードに別のエラーが発生し、1 つまたは 2 つのエラーが発生する可能性があります。これらのことに対してランダムという言葉を使用するのは好きではありませんが、それらを呼び出す特定のコード行を追跡できないようです.ISessionオブジェクトに関連するコード行に表示されるようです. Global.asax ファイルの BeginTranscation メソッドで "Session is closed" 例外がスローされたことさえあります。

アプリケーションは、hibernate.cfg.xml で current_session_context_class の Web オプションを使用します。

私の疑いは、それが私のセッション管理コードに関連していることです。Web サイトは通常、約 10 個の AJAX リクエストを同時にロードします。複数のページが同時にロードされると、エラーがより頻繁に発生するようです。使用されるデータベースごとに 1 つずつ、合計 2 つのセッション ファクトリがあります。

関連する Global.asax コードは次のとおりです。

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            _sessionFactory = (new WebClientSessionManager()).MakeSessionFactory();
            _sessionFactoryNotWeb = ClientSessionManager.MakeSessionFactory();
        }
protected void Application_BeginRequest(object sender, EventArgs e)
        {
            _session = _sessionFactory.OpenSession();
            _sessionNotWeb = _sessionFactoryNotWeb.OpenSession();
            CurrentSessionContext.Bind(_sessionNotWeb);
            CurrentSessionContext.Bind(_session);
            _session.BeginTransaction();
            _sessionNotWeb.BeginTransaction();
        }
 protected void Application_EndRequest(object sender, EventArgs e)
        {
            //Same code is repeated for the _sessionFactoryNotWeb
            ISession session = CurrentSessionContext.Unbind(_sessionFactory);
            if (session != null)
            {
                if (session.Transaction.IsActive)
                {
                    try
                    {
                        session.Transaction.Commit();
                    }
                    catch
                    {
                        session.Transaction.Rollback();
                    }
                }
                try
                {
                    session.Dispose();
                }
                catch
                {

                }
            }

NHibernate プロファイラーで実行されているページを見てきました。セッションが BeginTranscation で開始されない場合もあれば、コミットされない場合もあれば、どちらでもない場合もあります。そして最も不可解なことに、それらは 3 回開始されても終了しないことがあります。

ISession オブジェクトへのすべての呼び出しは、次のコードによって管理されます (ファクトリごとに 1 つずつあります)。

public static ISession WebSession()
        {
            if (CurrentSessionContext.HasBind(MvcApplication._sessionFactory))
            {
                if (MvcApplication._sessionFactory.GetCurrentSession().IsOpen)
                {
                    return MvcApplication._sessionFactory.GetCurrentSession();
                }
                else
                {
                    log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Unbinding NHibernate session");
                    CurrentSessionContext.Unbind(MvcApplication._sessionFactory);
                    return WebSession();
                }
            }
            else
            {
                log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Initialising NHibernate session");
                var session = MvcApplication._sessionFactory.OpenSession();
                CurrentSessionContext.Bind(session);
                session.BeginTransaction();
                return session;
            }
        }

上記のコードに従って、フラッシュ、コミット、セッションの破棄、および再オープンが行われない限り、アプリ全体で BeginTransaction または Commit への呼び出しはありません。皆さんがこれに当てることができる光は大歓迎です!

4

1 に答える 1

6

Global.asax.csのグローバル(アプリケーション全体)プロパティにセッションを保存しているようです。つまり、プロパティには、特定のリクエストのセッションではなく、最後に作成されたセッションが含まれます。同時に2つのリクエストがあり、作成したばかりのセッションにまだアクセスしているかどうかさえわかりません。これは、同じコードを実行している次のリクエストからの別のセッションによってすでに上書きされている可能性があるためです。Session-per-Requestパターンに従う場合は、Webリクエストとは関係のない場所にセッションを保存しないでください。たとえば、NHセッションをHttpContext.Items-Collectionに保存できます。MVCでのセッション管理の別の方法として、Ayendeは、リクエスト全体ではなく、単一のMVCアクションにセッション管理をラップする方法に関する良い例を投稿しました。

于 2013-01-21T07:25:08.803 に答える