4

ロールバックされたCastleActiveRecordTransactionScopeを使用する際の問題を解決しようとしています。

ロールバック後、Dogテーブルをクエリできません。「Dog.FindFirst()」行は、dogMissingNameを挿入できないため、「SlicedFindAllforDogを実行できませんでした」で失敗します。

using (new SessionScope())
{
    try
    {
        var trans = new TransactionScope(TransactionMode.New, OnDispose.Commit);

        try 
        {
             var dog = new Dog
             {
                 Name = "Snowy"
             };
             dog.Save();
             var dogMissingName = new Dog();
             dogMissingName.Save();
        }
        catch (Exception)
        {
           trans.VoteRollBack();
           throw;
        }
        finally
        {
           trans.Dispose();
        }   
     }
     catch (Exception ex)
     {
         var dogFromDatabase = Dog.FindFirst();
         Console.WriteLine("A dog: " + dogFromDatabase.Name);
     }
 }

スタックトレースは次のとおりです。

Castle.ActiveRecord.Framework.ActiveRecordException: Could not perform SlicedFindAll for Dog ---> NHibernate.Exceptions.GenericADOException: could not insert: [Mvno.Dal.Dog#219e86fa-1081-490a-92d1-9d480171fcfd][SQL: INSERT INTO Dog (Name, Id) VALUES (?, ?)] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Name', table 'Dog'; column does not allow nulls. INSERT fails.
The statement has been terminated.
   ved System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   ved System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   ved System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   ved System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   ved System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   ved System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   ved System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   ved System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   ved System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   ved NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)
   ved NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
   ved NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
   --- End of inner exception stack trace ---
   ved NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
   ved NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
   ved NHibernate.Action.EntityInsertAction.Execute()
   ved NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
   ved NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
   ved NHibernate.Engine.ActionQueue.ExecuteActions()
   ved NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
   ved NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event)
   ved NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces)
   ved NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)
   ved NHibernate.Impl.CriteriaImpl.List(IList results)
   ved NHibernate.Impl.CriteriaImpl.List()
   ved Castle.ActiveRecord.ActiveRecordBase.SlicedFindAll(Type targetType, Int32 firstResult, Int32 maxResults, Order[] orders, ICriterion[] criteria)
   --- End of inner exception stack trace ---
   ved Castle.ActiveRecord.ActiveRecordBase.SlicedFindAll(Type targetType, Int32 firstResult, Int32 maxResults, Order[] orders, ICriterion[] criteria)
   ved Castle.ActiveRecord.ActiveRecordBase.FindFirst(Type targetType, Order[] orders, ICriterion[] criteria)
   ved Castle.ActiveRecord.ActiveRecordBase.FindFirst(Type targetType, ICriterion[] criteria)
   ved Castle.ActiveRecord.ActiveRecordBase`1.FindFirst(ICriterion[] criteria)
4

2 に答える 2

3

スタックトレースを見ると、挿入の最初の試行が失敗した後でも、無効なdogMissingNameレコードがセッションのバッチ挿入バッファに残っていることがわかります。Dog.FindFirst()同じセッションの後半で呼び出すと、内部が再トリガーされますFlush()(失敗した挿入が再度試行されます)。

ドキュメントのセクション9.6から:

ISessionは、ADO.NET接続の状態をメモリに保持されているオブジェクトの状態と同期するために必要なSQLステートメントを実行することがあります。このプロセス、フラッシュは、デフォルトで次のポイントで発生します

  • Find()またはEnumerable()のいくつかの呼び出しから
  • NHibernate.ITransaction.Commit()から
  • ISession.Flush()から

さらに、ドキュメントのセクション9.7.2から:

トランザクションをロールバックする場合は、NHibernateの内部状態が一貫していることを確認するために、現在のセッションをすぐに閉じて破棄する必要があります。

単に最も外側の/の内側に移動using (new SessionScope()) trycatchすることは、実行可能な回避策である可能性があります(最初の挿入は失敗し、例外をSessionScope発生させて、同じ挿入で2番目の失敗をトリガーする可能性があり、最後に失敗しますcatch- 「データは。)のSessionScope.Flush()"ではフラッシュされませcom.googlegroups.castle-project-users

または、セッションを閉じたくない場合は、セッションのデフォルトのフラッシュ動作(クラスを参照)を変更して、明示的に呼び出されない限り(コミットする前など)フラッシュされないようにすることができます。ただし、この方法では、複雑でエラーが発生しやすくなります。FlushModeFlush()

于 2010-04-08T02:37:29.527 に答える
2

重要なのはVladの答えです。

トランザクションをロールバックする場合は、NHibernateの内部状態が一貫していることを確認するために、現在のセッションをすぐに閉じて破棄する必要があります。

それを理解して適用すると、コードは次のようになります。

try
{
    using (new SessionScope())
    using (var trans = new TransactionScope(TransactionMode.New, OnDispose.Commit))
    {
        try 
        {
            var dog = new Dog { Name = "Snowy" };
            dog.Save();
            var dogMissingName = new Dog();
            dogMissingName.Save();
        }
        catch (Exception)
        {
            trans.VoteRollBack();
            throw;
        }
    }
}
catch (Exception ex)
{
    using (new SessionScope())
    {
        var dogFromDatabase = Dog.FindFirst();
        Console.WriteLine("A dog: " + dogFromDatabase.Name);
    }
}
于 2010-04-09T21:45:35.597 に答える