Castle ActiveRecord TransactionScope 失败导致以后的查询无效
我试图解决使用回滚的 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);
}
}
堆栈跟踪如下:
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您查看堆栈跟踪,您会发现无效的
dogMissingName
记录仍然徘徊在会话的批量插入缓冲区中,即使在第一次尝试执行插入失败。稍后在同一会话中调用Dog.FindFirst()
会重新触发内部Flush()
(再次尝试失败的插入。)来自文档第 9.6 节:
另外,来自 第 9.7.2 节文档的:
只需移动
使用 (new SessionScope())
内部最外面的try
/catch
可能是一个可行的解决方法(初始插入将失败,引发异常,这将使您退出 SessionScope ,可能会在同一个插入上触发第二次失败,您最终捕获< /code> - 另请参阅
“数据未在 SessionScope.Flush() 上刷新” >com.googlegroups.castle-project-users
。)。或者,如果您不想关闭会话,您应该能够简单地更改会话默认刷新行为(请参阅
FlushMode
类),这样它就不会刷新,除非显式调用Flush()
(例如在提交之前)。请注意,以这种方式管理刷新很快就会变得复杂且容易出错,不过。If you look at the stack trace, you'll realize that the invalid
dogMissingName
record is still hanging around in the session's batched insertion buffer, even after the first attempt to perform the insertion failed. CallingDog.FindFirst()
later in the same session re-triggers an internalFlush()
(which attempts once again the failed insert.)From section 9.6 of the documentation:
Additionally, from section 9.7.2 of the documentation:
Simply moving
using (new SessionScope())
inside the outermosttry
/catch
may be a viable workaround (the initial insertion will fail, raise an exception which will take you out of theSessionScope
, likely triggering a second failure on the same insert, failure which you finallycatch
-- also see "data is not flushed on SessionScope.Flush()" incom.googlegroups.castle-project-users
.).Alternatively, if you don't want to close the session, you should be able to simply change the session default flush behaviour (see the
FlushMode
class) so that it never flushes unlessFlush()
is called explicitly (e.g. before committing.) Note that managing flushing in this way will get complex and error-prone very quickly, though.关键就在弗拉德的回答上:
理解并应用这一点后,您的代码应如下所示:
The key is right on Vlad's answer:
After you understand and apply that, your code should then look like this: