2010-03-29 2 views
4

Я пытаюсь решить проблему при использовании восстановленного TransactiveScope Castle ActiveRecord.Неудавшийся замок ActiveRecord TransactionScope приводит к недействительности будущих запросов

После откат я не могу запросить таблицу Dog. Ошибка строки «Dog.FindFirst()» «Не удалось выполнить SlicedFindAll для Dog», поскольку она не может вставить имя dogMissingName.

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

StackTrace выглядит следующим образом:

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) 
+0

пожалуйста, напишите полный стек след –

+0

трассировки стека отвечал. – mbp

+0

Обратите внимание, что если я заменил внешний SessionScope на TransactionScope, он будет работать! – mbp

ответ

3

Если вы взгляд на трассировки стека, вы поймете, что недействительные dogMissingName запись еще торчать в пакетном буфере вставки на сессии, даже после первой попытки выполнить вставку не удалось. Вызов Dog.FindFirst() позже в том же сеансе повторно запускает внутренний Flush() (который пытается в очередной раз не удалось вставить.)

Из section 9.6 документации:

время от времени ISession будет выполнить операторы SQL, необходимые для , синхронизируют состояние соединения ADO.NET с состоянием объектов в памяти. Этот процесс, на одном уровне, происходит по умолчанию в следующих точках

  • от некоторых призываний Find() или Enumerable()
  • из NHibernate.ITransaction.Commit()
  • из ISession.Flush()

Кроме того, из section 9.7.2 документации:

Если вы отмените транзакцию, вы должны немедленно закрыть и отменить текущий сеанс , чтобы убедиться, что внутреннее состояние NHibernate согласовано.

Просто движущемся using (new SessionScope())внутри внешний try/catch может быть жизнеспособным обходной путь (начальная вставка потерпит неудачу, возбудит исключение, которое доставит вас из SessionScope, вероятно, запуская вторую неудачу на такая же вставка, отказ, который вы, наконец, catch - также см. "data is not flushed on SessionScope.Flush()" в com.googlegroups.castle-project-users.).

В качестве альтернативы, если вы не хотите, чтобы закрыть сессию, вы должны быть в состоянии просто change the session default flush behaviour (см FlushMode класс) так, чтобы он никогда не смывает, если Flush() не вызывается явно (например, перед совершением.) Обратите внимание, что таким образом, управление промывкой будет очень сложным и подверженным ошибкам.

2

Ключ прямо на ответ Влада:

Если вы откатить транзакцию вы должны немедленно закрыть и выбросить текущий сеанс, чтобы гарантировать, что внутреннее состояние 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); 
    } 
} 
Смежные вопросы