目標:
創建一個父子關係,以便對父級子級列表的修改將傳播給所有子級,並讓NHibernate完成繁重工作。 在自引用表上,父子關係將爲Has-Many
。NHibernate有多個級聯刪除集合失敗
問題:
任何刪除父(根)對象的嘗試都會導致異常,而不是刪除子對象的預期行爲。東西
版本我使用:
的Microsoft SQL Server Management Studio中版本10.0.4064.0
FluentNHibernate 1.3版
NHibernate的版本3.2.0.4
下面是一組當前類對象和表結構我的我正在使用來複制這種行爲。
// Entity
class Task
{
ID { get; set; }
public virtual IList<Task> Children { get; set; }
public virtual byte[] Version { get; protected set; }
public virtual bool IsNew() { return ID <= 0; }
public Task()
{
this.Children = new System.Collections.Generic.List<Task>();
}
// Other properties excluded for brevity
}
// Map
class TaskMap : ClassMap<Task>
{
TaskMap()
{
Table("Task");
Id(x => x.ID, "ID")
.GeneratedBy.HiLo(
"NH_HiLo", "NextHigh", "100",
string.Format("TableName = '{0}'", "Task"));
HasMany<Task>(x => x.Children)
.KeyColumn("ParentTaskID")
.Cascade.AllDeleteOrphan();
// Other properties omitted for brevity
Version(x => x.Version)
.Not.Nullable()
.Generated.Always()
.Column("Version")
.CustomSqlType("timestamp");
}
}
// Repository Delete Method:
public virtual void Delete(Task value)
{
// CurrentSession is an ISession object that is currently open
using (var transaction = CurrentSession.BeginTransaction())
{
try
{
CurrentSession.Delete(value);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
// Test Case using NUnit.Framework and FluentNHibernate.Testing:
[TestFixtureSetUp]
public void SetUpFixture()
{
_repository = new Repository();
}
[Test]
public void MappingTest()
{
var task = new Task(); // Omitted assigning other properties for brevity
var entity = new Task(); // Omitted assigning other properties for brevity
entity.Children.Add(task);
_entity = new PersistenceSpecification<Task>(_repository.CurrentSession)
.VerifyTheMappings(entity);
}
[TearDown]
public void TearDown()
{
if (_entity != null && !_entity.IsNew())
{
_repository.Delete(_entity);
_entity = null;
}
}
--Table Script:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Task](
[ID] [bigint] NOT NULL,
[ParentTaskID] [bigint] NULL, -- Notice it DOES HAVE a NULLable FK reference.
[Version] [timestamp] NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED(
[ID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Task] WITH CHECK ADD CONSTRAINT [FK_TasksChild_TasksParent]
FOREIGN KEY([ParentTaskID])
REFERENCES [dbo].[Task] ([ID]) -- Notice the self table reference for child objects
GO
ALTER TABLE [dbo].[Task] CHECK CONSTRAINT [FK_TasksChild_TasksParent]
GO
使用上面的表和類,將級聯更改爲這些選項並在執行拆除測試期間執行指定時,這些就是結果。
隨着Cascade.AllDeleteOrphan:
簡單地調用刪除父對象上我得到這個異常:
NHibernate.StaleObjectStateException was unhandled by user code
Message=Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Task#1015859]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2178
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Int32 j, Object obj, SqlCommandInfo sql, ISessionImplementor session, Object[] loadedState) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
通過每個孩子的迭代後,通過這些孩子的孩子挖遞歸併試圖從下往上刪除每個孩子:
NHibernate.ObjectDeletedException was unhandled by user code
Message=deleted object would be re-saved by cascade (remove deleted object from associations)[Task#1016061]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Impl.SessionImpl.ForceFlush(EntityEntry entityEntry) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 914
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 140
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 76
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultSaveOrUpdateEventListener.cs:line 53
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 2662
at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 549
at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\CascadingAction.cs:line 249
at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 216
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 181
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 240
at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 201
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 185
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\Cascade.cs:line 126
at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 207
at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 197
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 48
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 18
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 25
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 25
at Tests.TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
通過迭代孩子,清算他們的每一個組的孩子之後,然後保存/刪除父我得到這個異常:
NHibernate.StaleObjectStateException was unhandled by user code
Message=Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Task#1015960]
Source=NHibernate
EntityName=Entities.Task
StackTrace:
at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2178
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Int32 j, Object obj, SqlCommandInfo sql, ISessionImplementor session, Object[] loadedState) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..\Repositories\Repository.cs:line 22
at TaskTest.TearDown() in ..\Tests\TaskTest.cs:line 76
隨着Cascade.All,只是要求刪除的父對象我得到這個異常:
同樣適用於Cascade.AllDeleteOrphan
迭代通過每個孩子後,遞歸挖掘這些孩子的孩子並試圖刪除從下到上每個孩子:
同樣爲級聯。AllDeleteOrphan
通過孩子迭代,清除他們的每一個組的孩子之後,然後保存/刪除父我得到這個異常:
也不例外:父被正確地刪除,但現在我已經成爲孤兒的我就不說了對象想!
我看了很多博客/ stackoverflow的問題/資源文檔,並沒有真正看到這個問題的解決方案。
這裏有一些我已經通過已經挖好的鏈接:
- How to delete a referenced object using FluentNHibernate (ye olde "deleted object would be resaved by cascade")
- Error in Cascade : deleted object would be re-saved by cascade
- Setting up Fluent NHibernate one-to-many with cascading deletes using the automapper
- key-many-to-one and key-property association: nhibernate won't DELETE items from set
- https://nhibernate.jira.com/browse/NH-1050
(注意我有空FK) - Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
(注意我用的時間戳樂觀版本) - https://forum.hibernate.org/viewtopic.php?t=933496
(評論由ernst_pluess約級聯=全扔了有關使用高住低練的警告標誌,但高住低練生成或分配?因爲從技術上NHibernate的生成它,然後分配給它...) - Exception deleting child records in NHibernate
(使它看起來像我不得不手動刪除所有子對象,它帶走了具有級聯的目的!!)
許多帖子提到顛倒了關係,但是設置逆並使孩子擁有這種關係在這裏完全不是目標!
我不知道我在想什麼,但希望這是一個很容易解決的問題,我忽略了。任何幫助都感激不盡!
我很感謝你的回覆。這不完全是我希望聽到的,因爲我希望我不必手動打破父母和孩子之間的關係,但看起來它是不可避免的。我確實通過添加一個ParentTask引用並調整delete方法遞歸地遍歷子列表來調用測試,方法是將父列表設置爲null以打破平局,最後刪除當Cascade.AllDeleteOrphan處於打開狀態時的父任務刪除所有孤立的任務。再次感謝您的回覆。 –