NHibernate有许多带有级联删除的集合正在失败



目标:
创建父子关系,这样对父母的子女列表的修改将传播到所有子女,并让NHibernate承担重任。父子关系将是自引用表上的Has-Many

问题:
任何删除父对象(根对象)的尝试都会导致异常,而不是删除子对象的预期行为。

我正在使用的东西的版本:
Microsoft SQL Server Management Studio版本10.0.406.0
FluentHibernate版本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

使用上面的表和类,将级联更改为这些选项,并在测试拆卸期间执行指定的,这些就是结果。


带级联。AllDeleteOrphan:
只需在父对象上调用delete,我就会得到这个异常:

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:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.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:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:CSharpNHNHnhibernatesrcNHibernateActionEntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultAbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:CSharpNHNHnhibernatesrcNHibernateTransactionAdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..RepositoriesRepository.cs:line 22
at TaskTest.TearDown() in ..TestsTaskTest.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:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 914
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultSaveOrUpdateEventListener.cs:line 140
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultSaveOrUpdateEventListener.cs:line 76
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultSaveOrUpdateEventListener.cs:line 53
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event) in d:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 2662
at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj) in d:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 549
at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascadingAction.cs:line 249
at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 216
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 181
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 240
at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 201
at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 185
at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 148
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object     anything) in d:CSharpNHNHnhibernatesrcNHibernateEngineCascade.cs:line 126
at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultAbstractFlushingEventListener.cs:line 207
at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultAbstractFlushingEventListener.cs:line 197
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultAbstractFlushingEventListener.cs:line 48
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultFlushEventListener.cs:line 18
at NHibernate.Impl.SessionImpl.Flush() in d:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:CSharpNHNHnhibernatesrcNHibernateTransactionAdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..RepositoriesRepository.cs:line 22
at Repositories.Repository.Delete(Task value) in ..RepositoriesRepository.cs:line 25
at Repositories.Repository.Delete(Task value) in ..RepositoriesRepository.cs:line 25
at Tests.TaskTest.TearDown() in ..TestsTaskTest.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:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.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:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.cs:line 2912
at NHibernate.Persister.Entity.AbstractEntityPersister.Delete(Object id, Object version, Object obj, ISessionImplementor session) in d:CSharpNHNHnhibernatesrcNHibernatePersisterEntityAbstractEntityPersister.cs:line 3095
at NHibernate.Action.EntityDeleteAction.Execute() in d:CSharpNHNHnhibernatesrcNHibernateActionEntityDeleteAction.cs:line 70
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 136
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 126
at NHibernate.Engine.ActionQueue.ExecuteActions() in d:CSharpNHNHnhibernatesrcNHibernateEngineActionQueue.cs:line 174
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultAbstractFlushingEventListener.cs:line 249
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) in d:CSharpNHNHnhibernatesrcNHibernateEventDefaultDefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in d:CSharpNHNHnhibernatesrcNHibernateImplSessionImpl.cs:line 1489
at NHibernate.Transaction.AdoTransaction.Commit() in d:CSharpNHNHnhibernatesrcNHibernateTransactionAdoTransaction.cs:line 190
at Repositories.Repository.Delete(Task value) in ..RepositoriesRepository.cs:line 22
at TaskTest.TearDown() in ..TestsTaskTest.cs:line 76

带级联。所有,只需在父对象上调用delete,我就会得到这个异常:
与Cascade相同。全部删除孤立

在迭代每个子级之后,递归地挖掘这些子级的子级,并尝试从下到上删除每个子级:
与Cascade相同。全部删除孤立

在遍历子对象,清除它们的每个子对象集,然后保存/删除父对象后,我得到了这个异常:
没有异常:父对象被正确删除,但现在我有了我不想要的孤立对象!


我浏览了许多博客/stackoverflow问题/资源文档,还没有真正看到这个问题的解决方案
以下是我已经挖掘过的几个链接:

  • 如何使用FluentHibernate删除引用的对象(旧的"已删除的对象将通过级联重新保存")
  • 级联中的错误:已删除的对象将被级联重新保存
  • 使用automapper通过级联删除设置Fluent NHibernate一对多
  • 密钥多对一和密钥属性关联:nhibernate赢得';t从集合中删除项目
  • https://nhibernate.jira.com/browse/NH-1050
    (注意,我的FK为空)
  • 行已被另一个事务更新或删除(或未保存的值映射不正确)
    (请注意,我使用时间戳乐观版本控制)
  • https://forum.hibernate.org/viewtopic.php?t=933496
    (ernst_plusess关于cascade=all的评论抛出了一个关于使用HiLo的警告标志,但HiLo是生成的还是分配的?因为从技术上讲,NHibernate生成它,然后分配它…)
  • 在NHibernate中删除子记录时出现异常
    (这让我不得不手动删除所有子对象,这就剥夺了级联的目的!!)

许多帖子都提到了反转关系,但设置。反转并让孩子拥有关系完全不是这里的目标!

我不知道我错过了什么,但希望这是一件非常简单的事情,我忽略了。任何帮助都将不胜感激!

您没有错过任何东西。这是您的映射的组合:a)子项没有映射Parent,b)子项已版本化,c)集合未设置为反向(因为没有映射的父项,子项无法管理它)d)最后,很可能是由于错误。

发生的情况是,通过版本控制,任何INSERT或UPDATE语句后面都跟着SELECT。。。以获取DB Server生成的最新CCD_ 2。但这种情况不会发生在一个案例中:

  1. 已插入集合父项
  2. 从数据库中选择的父版本
  3. 插入的子项
  4. 从数据库中选择的子版本
  5. 子项已更新(无反转)到引用父项
    • -什么都没有-没有选择子版本

因为关系更新后的子版本与DB中刚刚增加的版本不同。。。稍后将抛出StaleException。

你能做的最好的事情就是扩展映射以拥有一个Parent。。。并使其反向

相关内容

  • 没有找到相关文章

最新更新