Cel:
Stwórz relację rodzic-dziecko, tak aby modyfikacje listy dzieci nadrzędnych rozprzestrzeniły się na wszystkie dzieci i mają NHibernate do ciężkiego podnoszenia. relacji rodzic-dziecko będzie Has-Many
na stole własnym odsyłania.NHibernate Has-Many Kolekcja z usuwaniem kaskadowym kończy się niepowodzeniem
Problem:
Każda próba usunięcia obiektu nadrzędnego (głównego) powoduje wyjątki zamiast oczekiwanego zachowania obiektów potomnych.
Wersje rzeczy używam:
Microsoft SQL Server Management Studio Wersja 10.0.4064.0
FluentNhibernate Wersja 1.3
NHibernate wersja 3.2.0.4
Poniżej znajduje się zestaw aktualnych klasy obiektów i struktury tabeli I używam do replikowania tego zachowania.
// 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
W powyższej tabeli i klasach, zmiana kaskady na te opcje i wykonanie określonej podczas rozłączenia testu, są to wyniki.
Z Cascade.AllDeleteOrphan:
prostu dzwoniąc usunąć z obiektu nadrzędnego otrzymuję ten wyjątek:
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
Po iteracja każdego z dzieci, rekurencyjnie kopanie przez dzieci tych dzieci i próba usunięcia każdego dziecka od dołu:
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
Po iteracja dzieci, wyczyszczenie każdego ze swoich zbiorów dzieci, a następnie zapisywanie/usuwanie rodzica otrzymuję ten wyjątek:
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
Z Cascade.All, po prostu dzwoniąc na usunięcie obiekt nadrzędny otrzymuję ten wyjątek:
samo jak dla Cascade.AllDeleteOrphan
Po iteracja każdego z dzieci, rekurencyjnie kopanie przez dzieci tych dzieci i próbuje dele Te każde dziecko od dołu do góry:
samo jak dla Cascade.AllDeleteOrphan
Po iteracja dzieci, wyczyszczenie każdego ze swoich zbiorów dzieci, a następnie zapisywanie/usuwanie rodzica otrzymuję ten wyjątek:
bez wyjątku: Rodzic zostanie usunięty poprawnie, ale teraz mam osierocony przedmiotów, że nie chcieć!
Mam przejrzałem wiele blogów/stackoverflow pytania/dokumenty zasobów i naprawdę nie widziałem rozwiązania tego problemu.
Oto kilka linków Mam wykopanych przez już:
- 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
(zauważyć mam zerowy FK) - Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
(zauważ używam datownika optymistyczne wersjonowanie) - https://forum.hibernate.org/viewtopic.php?t=933496
(komentarz przez ernst_pluess o kaskadzie = wszystko rzucił flagę ostrzegawczą o używaniu Hilo, ale jest HiLo generowane lub przypisane? bo technicznie NHibernate generuje go następnie przypisuje go ...) - Exception deleting child records in NHibernate
(sprawia, że wydaje się, jakbym musiał ręcznie usunąć wszystkie obiekty podrzędne, który gładzi cel mający kaskadę !!)
Wiele postów wspomina o odwracaniu związku, ale ustawienie. Odwrotność i sprawienie, że dziecko jest właścicielem związku, nie jest tutaj celem!
Nie mam pojęcia, czego mi brakuje, ale mam nadzieję, że jest to coś naprawdę prostego do naprawienia, które przeoczyłem. Każda pomoc będzie doceniona!
Doceniam twoją odpowiedź. Nie było to dokładnie to, co miałem nadzieję usłyszeć, ponieważ miałem nadzieję, że nie muszę ręcznie zerwać związku między rodzicem a dzieckiem, ale wygląda na to, że jest nieuniknione. Otrzymałem test do przekazania, dodając odwołanie do ParentTask i ulepszając metodę delete, aby rekursywnie przeglądać listy potomne ustawiając parent na wartość null, aby zerwać remis i ostatecznie usunąć zadanie nadrzędne, które po włączeniu Cascade.AllDeleteOrphan jest prawidłowe usuwa wszystkie osierocone zadania. Jeszcze raz, dziękuję za odpowiedź. –