2012-12-13 38 views
6

目標:
創建一個父子關係,以便對父級子級列表的修改將傳播給所有子級,並讓NHibernate完成繁重工作。 在自引用表上,父子關係將爲Has-ManyNHibernate有多個級聯刪除集合失敗

問題:
任何刪除父(根)對象的嘗試都會導致異常,而不是刪除子對象的預期行爲。東西

版本我使用:
的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的問題/資源文檔,並沒有真正看到這個問題的解決方案。
這裏有一些我已經通過已經挖好的鏈接:


許多帖子提到顛倒了關係,但是設置逆並使孩子擁有這種關係在這裏完全不是目標!

我不知道我在想什麼,但希望這是一個很容易解決的問題,我忽略了。任何幫助都感激不盡!

回答

5

你是不是缺少任何東西。它是你映射的組合:a)孩子沒有父映射,b)孩子是版本化的,c)集合沒有被設置爲反向(因爲它不能由沒有映射父親的孩子管理)d)最後,最可能是由於一個錯誤。

會發生什麼情況,使用版本控制時,任何INSERT或UPDATE語句後跟SELECT ...以獲取DB Server生成的最新timestamp。但是,這並不在一種情況下發生:

  1. 收集父插入從數據庫中選擇
  2. 父版本從DB選擇
  3. 孩子插入
  4. 子版本
  5. 子更新(無倒置)參考父母
    • - 沒有 - ch未選擇版本...

因爲關係更新後的子版本是不同的,那麼只是遞增DB中的一個......後來StaleException被拋出。

你可以做的最好的事情是擴大映射,讓父母......並使其逆轉

+0

我很感謝你的回覆。這不完全是我希望聽到的,因爲我希望我不必手動打破父母和孩子之間的關係,但看起來它是不可避免的。我確實通過添加一個ParentTask引用並調整delete方法遞歸地遍歷子列表來調用測試,方法是將父列表設置爲null以打破平局,最後刪除當Cascade.AllDeleteOrphan處於打開狀態時的父任務刪除所有孤立的任務。再次感謝您的回覆。 –