我正在尝试使用回滚的Castle ActiveRecord TransactionScope解决问题。
回滚后,我无法查询Dog表。 “Dog.FindFirst()”行失败,“无法对Dog执行SlicedFindAll”,因为它无法插入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 :(得分: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
- 也会在com.googlegroups.castle-project-users
中看到"data is not flushed on SessionScope.Flush()" )。
或者,如果您不想关闭会话,则应该能够简单地change the session default flush behaviour (请参阅FlushMode
类),以便它永远不会刷新,除非Flush()
被明确调用(例如在提交之前。)请注意,以这种方式管理刷新会很快变得复杂且容易出错。
答案 1 :(得分: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);
}
}