From d7763b216096a845ba61e97aba2d4577cbe44112 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Wed, 9 Mar 2022 12:47:08 +0100 Subject: [PATCH 01/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommand.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs new file mode 100644 index 0000000000..fc58bb6368 --- /dev/null +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -0,0 +1,134 @@ +// This file is part of the re-motion Core Framework (www.re-motion.org) +// Copyright (c) rubicon IT GmbH, www.rubicon.eu +// +// The re-motion Core Framework is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// re-motion is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with re-motion; if not, see http://www.gnu.org/licenses. +// +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using JetBrains.Annotations; +using Remotion.Data.DomainObjects.DataManagement.RelationEndPoints; +using Remotion.Data.DomainObjects.Infrastructure; +using Remotion.Data.DomainObjects.Infrastructure.InvalidObjects; +using Remotion.Utilities; + +namespace Remotion.Data.DomainObjects.DataManagement.Commands +{ + public class UnloadFilteredDomainObjectsCommand : IDataManagementCommand + { + public IRelationEndPointManager RelationEndPointManager { get; } + public DataContainerMap DataContainerMap { get; } + public IInvalidDomainObjectManager InvalidDomainObjectManager { get; } + public RelationEndPointMap RelationEndPointMap { get; } + public IRelationEndPointRegistrationAgent RelationEndPointRegistrationAgent { get; } + public IClientTransactionEventSink TransactionEventSink { get; } + public Predicate DomainObjectFilter { get; } + private IReadOnlyCollection _dataContainersForUnloading = Array.Empty(); + private IReadOnlyCollection _virtualEndPointsForUnloading = Array.Empty(); + private IReadOnlyCollection _realObjectEndPointsForUnloading = Array.Empty(); + + public UnloadFilteredDomainObjectsCommand ( + IRelationEndPointManager relationEndPointManager, + DataContainerMap dataContainerMap, + IInvalidDomainObjectManager invalidDomainObjectManager, + RelationEndPointMap relationRelationEndPointMap, + IRelationEndPointRegistrationAgent relationEndPointRegistrationAgent, + IClientTransactionEventSink transactionEventSink, + Predicate domainObjectFilter) + { + ArgumentUtility.CheckNotNull("relationEndPointManager", relationEndPointManager); + ArgumentUtility.CheckNotNull("dataContainerMap", dataContainerMap); + ArgumentUtility.CheckNotNull("invalidDomainObjectManager", invalidDomainObjectManager); + ArgumentUtility.CheckNotNull("relationRelationEndPointMap", relationRelationEndPointMap); + ArgumentUtility.CheckNotNull("relationEndPointRegistrationAgent", relationEndPointRegistrationAgent); + ArgumentUtility.CheckNotNull("transactionEventSink", transactionEventSink); + ArgumentUtility.CheckNotNull("domainObjectFilter", domainObjectFilter); + + RelationEndPointManager = relationEndPointManager; + DataContainerMap = dataContainerMap; + InvalidDomainObjectManager = invalidDomainObjectManager; + RelationEndPointMap = relationRelationEndPointMap; + RelationEndPointRegistrationAgent = relationEndPointRegistrationAgent; + TransactionEventSink = transactionEventSink; + DomainObjectFilter = domainObjectFilter; + } + + public IEnumerable GetAllExceptions () + { + return Enumerable.Empty(); + } + + public void Begin () + { + _dataContainersForUnloading = DataContainerMap + .Where(dc => DomainObjectFilter(dc.DomainObject)) + .ToList() + .AsReadOnly(); + + var relationEndPoints = GetRelationEndPointsForUnloading(_dataContainersForUnloading); + _realObjectEndPointsForUnloading = relationEndPoints.RealObjectEndPoints; + _virtualEndPointsForUnloading = relationEndPoints.VirtualEndPoints; + if (_dataContainersForUnloading.Count > 0) + TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc => dc.DomainObject).ToList()); + } + + public void Perform () + { + // Unregistering a real-object-endpoint also unregisters the associated virtual endpoint. + // If is therefor best to first handle all real-object-endpoints in case both sides belong to the same object. + + foreach (var relationEndPoint in _realObjectEndPointsForUnloading) + { + RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); + } + + foreach (var relationEndPoint in _virtualEndPointsForUnloading) + { + relationEndPoint.MarkDataIncomplete(); + Assertion.IsTrue(relationEndPoint.CanBeCollected, "VirtualEndPoint '{0}' cannot be collected.", relationEndPoint.ID); + RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); + } + + foreach (var dataContainer in _dataContainersForUnloading) + { + DataContainerMap.Remove(dataContainer.ID); + + var dataContainerState = dataContainer.State; + dataContainer.Discard(); + if (dataContainerState.IsNew) + InvalidDomainObjectManager.MarkInvalid(dataContainer.DomainObject); + } + } + + public void End () + { + if (_dataContainersForUnloading.Count > 0) + TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc=>dc.DomainObject).ToList()); + } + + public ExpandedCommand ExpandToAllRelatedObjects () + { + return new ExpandedCommand(this); + } + + private (IReadOnlyCollection RealObjectEndPoints, IReadOnlyCollection VirtualEndPoints) GetRelationEndPointsForUnloading ( + IReadOnlyCollection dataContainersForUnloading) + { + // if (virtualEndPoint.CanBeCollected) + + throw new NotImplementedException(); + } + } +} From dc9041015e59edf04c5f1d8db0e8a56b8b3efd69 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Wed, 9 Mar 2022 16:16:55 +0100 Subject: [PATCH 02/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommand.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index fc58bb6368..c1e6e1e964 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -91,11 +91,13 @@ public void Perform () foreach (var relationEndPoint in _realObjectEndPointsForUnloading) { + RelationEndPointMap.RemoveEndPoint(relationEndPoint.ID); RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); } foreach (var relationEndPoint in _virtualEndPointsForUnloading) { + RelationEndPointMap.RemoveEndPoint(relationEndPoint.ID); relationEndPoint.MarkDataIncomplete(); Assertion.IsTrue(relationEndPoint.CanBeCollected, "VirtualEndPoint '{0}' cannot be collected.", relationEndPoint.ID); RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); @@ -124,11 +126,33 @@ public ExpandedCommand ExpandToAllRelatedObjects () } private (IReadOnlyCollection RealObjectEndPoints, IReadOnlyCollection VirtualEndPoints) GetRelationEndPointsForUnloading ( - IReadOnlyCollection dataContainersForUnloading) + IReadOnlyCollection dataContainers) { + var realObjectEndPoints = new HashSet(); + var virtualObjectEndPoints = new HashSet(); + + var endPoints = dataContainers + .SelectMany(dc => dc.AssociatedRelationEndPointIDs.Select(endPointID => RelationEndPointManager.GetRelationEndPointWithoutLoading(endPointID))) + .Where(ep => ep != null) + .Select(ep => ep!) + .Where(ep => !ep.IsNull); + + foreach (var endPoint in endPoints) + { + if (endPoint is IRealObjectEndPoint realObjectEndPoint) + { + realObjectEndPoints.Add(realObjectEndPoint); + } + else + { + var virtualEndPoint = (IVirtualEndPoint)endPoint; + virtualObjectEndPoints.Add(virtualEndPoint); + } + } + // if (virtualEndPoint.CanBeCollected) - throw new NotImplementedException(); + return (RealObjectEndPoints: realObjectEndPoints, VirtualEndPoints: virtualObjectEndPoints); } } } From bcf1dcdacdb1c8312f32233bd3ca1949763c966d Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Thu, 10 Mar 2022 17:06:29 +0100 Subject: [PATCH 03/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommand.cs | 140 +++++++++++++++--- 1 file changed, 120 insertions(+), 20 deletions(-) diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index c1e6e1e964..8649026c55 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -16,12 +16,12 @@ // using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -using JetBrains.Annotations; using Remotion.Data.DomainObjects.DataManagement.RelationEndPoints; using Remotion.Data.DomainObjects.Infrastructure; using Remotion.Data.DomainObjects.Infrastructure.InvalidObjects; +using Remotion.Data.DomainObjects.Mapping; +using Remotion.FunctionalProgramming; using Remotion.Utilities; namespace Remotion.Data.DomainObjects.DataManagement.Commands @@ -38,6 +38,7 @@ public class UnloadFilteredDomainObjectsCommand : IDataManagementCommand private IReadOnlyCollection _dataContainersForUnloading = Array.Empty(); private IReadOnlyCollection _virtualEndPointsForUnloading = Array.Empty(); private IReadOnlyCollection _realObjectEndPointsForUnloading = Array.Empty(); + private List _exceptions = new List(); public UnloadFilteredDomainObjectsCommand ( IRelationEndPointManager relationEndPointManager, @@ -67,7 +68,7 @@ public UnloadFilteredDomainObjectsCommand ( public IEnumerable GetAllExceptions () { - return Enumerable.Empty(); + return _exceptions.AsReadOnly(); } public void Begin () @@ -78,14 +79,25 @@ public void Begin () .AsReadOnly(); var relationEndPoints = GetRelationEndPointsForUnloading(_dataContainersForUnloading); - _realObjectEndPointsForUnloading = relationEndPoints.RealObjectEndPoints; - _virtualEndPointsForUnloading = relationEndPoints.VirtualEndPoints; - if (_dataContainersForUnloading.Count > 0) - TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc => dc.DomainObject).ToList()); + if (relationEndPoints.RealObjectEndPointsNotPartOfUnloadSet.Any() || relationEndPoints.VirtualEndPointsNotPartOfUnloadSet.Any()) + { + _exceptions.Add(new InvalidOperationException("Transitive closure violated")); + throw _exceptions.First(); + } + else + { + _realObjectEndPointsForUnloading = relationEndPoints.RealObjectEndPoints; + _virtualEndPointsForUnloading = relationEndPoints.VirtualEndPoints; + if (_dataContainersForUnloading.Count > 0) + TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc => dc.DomainObject).ToList()); + } } public void Perform () { + if (_exceptions.Count > 0) + throw _exceptions.First(); + // Unregistering a real-object-endpoint also unregisters the associated virtual endpoint. // If is therefor best to first handle all real-object-endpoints in case both sides belong to the same object. @@ -116,8 +128,11 @@ public void Perform () public void End () { + if (_exceptions.Count > 0) + throw _exceptions.First(); + if (_dataContainersForUnloading.Count > 0) - TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc=>dc.DomainObject).ToList()); + TransactionEventSink.RaiseObjectsUnloadingEvent(_dataContainersForUnloading.Select(dc => dc.DomainObject).ToList()); } public ExpandedCommand ExpandToAllRelatedObjects () @@ -125,34 +140,119 @@ public ExpandedCommand ExpandToAllRelatedObjects () return new ExpandedCommand(this); } - private (IReadOnlyCollection RealObjectEndPoints, IReadOnlyCollection VirtualEndPoints) GetRelationEndPointsForUnloading ( - IReadOnlyCollection dataContainers) + private + (HashSet RealObjectEndPoints, + HashSet VirtualEndPoints, + HashSet RealObjectEndPointsNotPartOfUnloadSet, + HashSet VirtualEndPointsNotPartOfUnloadSet) + GetRelationEndPointsForUnloading (IReadOnlyCollection dataContainers) { var realObjectEndPoints = new HashSet(); var virtualObjectEndPoints = new HashSet(); + var realObjectEndPointsNotPartOfUnloadSet = new HashSet(); + var virtualEndPointsNotPartOfUnloadSet = new HashSet(); + var objectIDs = dataContainers.ToDictionary(dc => dc.ID); - var endPoints = dataContainers - .SelectMany(dc => dc.AssociatedRelationEndPointIDs.Select(endPointID => RelationEndPointManager.GetRelationEndPointWithoutLoading(endPointID))) - .Where(ep => ep != null) - .Select(ep => ep!) - .Where(ep => !ep.IsNull); + var endPoints = RelationEndPointManager.RelationEndPoints + .ApplySideEffect(ep => Assertion.IsFalse(ep.IsNull, "RelationEndPoint '{0}' is a null end-point.", ep.ID)) + .Where(ep => ep.ObjectID != null); foreach (var endPoint in endPoints) { + Assertion.DebugAssert(endPoint.Definition.IsAnonymous == false, "endPoint.Definition.IsAnonymous == false"); + Assertion.DebugIsNotNull(endPoint.ObjectID, "endPoint.ObjectID != null"); + var isPartOfUnloadedSet = objectIDs.ContainsKey(endPoint.ObjectID); + if (endPoint is IRealObjectEndPoint realObjectEndPoint) { - realObjectEndPoints.Add(realObjectEndPoint); + if (isPartOfUnloadedSet) + { + if (realObjectEndPoint.IsNull) + { + realObjectEndPoints.Add(realObjectEndPoint); + } + else if ((realObjectEndPoint.OppositeObjectID == null || objectIDs.ContainsKey(realObjectEndPoint.OppositeObjectID)) + && (realObjectEndPoint.OriginalOppositeObjectID == null || objectIDs.ContainsKey(realObjectEndPoint.OriginalOppositeObjectID))) + { + realObjectEndPoints.Add(realObjectEndPoint); + } + else + { + realObjectEndPointsNotPartOfUnloadSet.Add(realObjectEndPoint); + var virtualEndPointID = realObjectEndPoint.GetOppositeRelationEndPointID(); + if (virtualEndPointID != null) + { + var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(virtualEndPointID); + if (virtualEndPoint != null) + virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + } + } + } } else { var virtualEndPoint = (IVirtualEndPoint)endPoint; - virtualObjectEndPoints.Add(virtualEndPoint); + if (isPartOfUnloadedSet) + { + if (virtualEndPoint.IsNull) + { + virtualObjectEndPoints.Add(virtualEndPoint); + } + + if (virtualEndPoint.CanBeCollected) + { + virtualObjectEndPoints.Add(virtualEndPoint); + } + else if (virtualEndPoint.Definition.Cardinality == CardinalityType.One) + { + var virtualObjectEndPoint = (IVirtualObjectEndPoint)virtualEndPoint; + if ((virtualObjectEndPoint.OppositeObjectID == null || objectIDs.ContainsKey(virtualObjectEndPoint.OppositeObjectID)) + && (virtualObjectEndPoint.OriginalOppositeObjectID == null || objectIDs.ContainsKey(virtualObjectEndPoint.OriginalOppositeObjectID))) + { + virtualObjectEndPoints.Add(virtualEndPoint); + } + else + { + virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + var realObjectEndPointID = virtualObjectEndPoint.GetOppositeRelationEndPointID(); + if (realObjectEndPointID != null) + { + var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(realObjectEndPointID); + if (oppositeRealObjectEndPoint != null) + realObjectEndPointsNotPartOfUnloadSet.Add(oppositeRealObjectEndPoint); + } + } + } + else + { + var collectionEndPoint = (ICollectionEndPoint)virtualEndPoint; + if (collectionEndPoint.GetData().All(obj => objectIDs.ContainsKey(obj.ID)) + && collectionEndPoint.GetOriginalData().All(obj => objectIDs.ContainsKey(obj.ID))) + { + virtualObjectEndPoints.Add(virtualEndPoint); + } + else + { + virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + var realObjectEndPointIDs = collectionEndPoint.GetOppositeRelationEndPointIDs().Where(epID => epID.ObjectID != null && !objectIDs.ContainsKey(epID.ObjectID)); + foreach (var realObjectEndPointID in realObjectEndPointIDs) + { + var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(realObjectEndPointID); + if (oppositeRealObjectEndPoint != null) + realObjectEndPointsNotPartOfUnloadSet.Add(oppositeRealObjectEndPoint); + } + } + } + } } } - // if (virtualEndPoint.CanBeCollected) - - return (RealObjectEndPoints: realObjectEndPoints, VirtualEndPoints: virtualObjectEndPoints); + return ( + RealObjectEndPoints: realObjectEndPoints, + VirtualEndPoints: virtualObjectEndPoints, + RealObjectEndPointsNotPartOfUnloadSet: realObjectEndPointsNotPartOfUnloadSet, + VirtualEndPointsNotPartOfUnloadSet: virtualEndPointsNotPartOfUnloadSet + ); } } } From 5fd2a571f29e50970f71b9740291890f5a8e8529 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Fri, 11 Mar 2022 09:02:52 +0100 Subject: [PATCH 04/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 26 +++++++++++++++++++ .../UnloadFilteredDomainObjectsCommand.cs | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs diff --git a/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs new file mode 100644 index 0000000000..8d7b93013e --- /dev/null +++ b/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs @@ -0,0 +1,26 @@ +// // This file is part of the re-motion Core Framework (www.re-motion.org) +// // Copyright (c) rubicon IT GmbH, www.rubicon.eu +// // +// // The re-motion Core Framework is free software; you can redistribute it +// // and/or modify it under the terms of the GNU Lesser General Public License +// // as published by the Free Software Foundation; either version 2.1 of the +// // License, or (at your option) any later version. +// // +// // re-motion is distributed in the hope that it will be useful, +// // but WITHOUT ANY WARRANTY; without even the implied warranty of +// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// // GNU Lesser General Public License for more details. +// // +// // You should have received a copy of the GNU Lesser General Public License +// // along with re-motion; if not, see http://www.gnu.org/licenses. +// // +using NUnit.Framework; + +namespace Remotion.Data.DomainObjects.UnitTests.DataManagement.Commands +{ + [TestFixture] + public class UnloadFilteredDomainObjectsCommandTest + { + + } +} diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index 8649026c55..23edcc0e91 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -198,8 +198,7 @@ public ExpandedCommand ExpandToAllRelatedObjects () { virtualObjectEndPoints.Add(virtualEndPoint); } - - if (virtualEndPoint.CanBeCollected) + else if (virtualEndPoint.CanBeCollected) { virtualObjectEndPoints.Add(virtualEndPoint); } From 17ce1b4327d88e9e5b0cf74bba46dae49d8835f5 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Fri, 11 Mar 2022 09:40:31 +0100 Subject: [PATCH 05/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 26 ---- .../UnloadFilteredDomainObjectsCommantTest.cs | 114 ++++++++++++++++++ .../TestableClientTransaction.cs | 2 +- .../UnloadFilteredDomainObjectsCommand.cs | 66 ++++++---- 4 files changed, 156 insertions(+), 52 deletions(-) delete mode 100644 Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs create mode 100644 Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs diff --git a/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs deleted file mode 100644 index 8d7b93013e..0000000000 --- a/Remotion/Data/DomainObjects.UnitTests/DataManagement/Commands/UnloadFilteredDomainObjectsCommantTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -// // This file is part of the re-motion Core Framework (www.re-motion.org) -// // Copyright (c) rubicon IT GmbH, www.rubicon.eu -// // -// // The re-motion Core Framework is free software; you can redistribute it -// // and/or modify it under the terms of the GNU Lesser General Public License -// // as published by the Free Software Foundation; either version 2.1 of the -// // License, or (at your option) any later version. -// // -// // re-motion is distributed in the hope that it will be useful, -// // but WITHOUT ANY WARRANTY; without even the implied warranty of -// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// // GNU Lesser General Public License for more details. -// // -// // You should have received a copy of the GNU Lesser General Public License -// // along with re-motion; if not, see http://www.gnu.org/licenses. -// // -using NUnit.Framework; - -namespace Remotion.Data.DomainObjects.UnitTests.DataManagement.Commands -{ - [TestFixture] - public class UnloadFilteredDomainObjectsCommandTest - { - - } -} diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs new file mode 100644 index 0000000000..777c7a8277 --- /dev/null +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -0,0 +1,114 @@ +// // This file is part of the re-motion Core Framework (www.re-motion.org) +// // Copyright (c) rubicon IT GmbH, www.rubicon.eu +// // +// // The re-motion Core Framework is free software; you can redistribute it +// // and/or modify it under the terms of the GNU Lesser General Public License +// // as published by the Free Software Foundation; either version 2.1 of the +// // License, or (at your option) any later version. +// // +// // re-motion is distributed in the hope that it will be useful, +// // but WITHOUT ANY WARRANTY; without even the implied warranty of +// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// // GNU Lesser General Public License for more details. +// // +// // You should have received a copy of the GNU Lesser General Public License +// // along with re-motion; if not, see http://www.gnu.org/licenses. +// // +using System; +using System.Linq; +using NUnit.Framework; +using Remotion.Data.DomainObjects.DataManagement; +using Remotion.Data.DomainObjects.DataManagement.Commands; +using Remotion.Data.DomainObjects.DataManagement.RelationEndPoints; +using Remotion.Data.DomainObjects.DomainImplementation; +using Remotion.Data.DomainObjects.Infrastructure.InvalidObjects; +using Remotion.Data.DomainObjects.UnitTests.TestDomain; +using Remotion.Development.UnitTesting; +using Remotion.Utilities; + +namespace Remotion.Data.DomainObjects.UnitTests.IntegrationTests.Unload +{ + [TestFixture] + public class UnloadFilteredTest: ClientTransactionBaseTest + { + [Test] + public void UnloadAllObjects_SingleDomainObject () + { + var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(order.State.IsNotLoadedYet, Is.True); + } + + [Test] + public void UnloadAllObjects_WithOneToOneRelation () + { + var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + order.OrderTicket.EnsureDataAvailable(); + + var orderTicket = order.OrderTicket; + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(order.State.IsNotLoadedYet, Is.True); + Assert.That(orderTicket.State.IsNotLoadedYet, Is.True); + } + + [Test] + public void UnloadAllObjects_WithDomainObjectCollectionRelation () + { + var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + order.OrderItems.EnsureDataComplete(); + + var orderItem = order.OrderItems.First(); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(order.State.IsNotLoadedYet, Is.True); + Assert.That(order.OrderItems.IsDataComplete, Is.False); + Assert.That(orderItem.State.IsNotLoadedYet, Is.True); + } + + + private void UnloadFiltered (Predicate domainObjectFilter) + { + var unloadCommand = CreateUnloadCommand(domainObjectFilter); + unloadCommand.Begin(); + unloadCommand.Perform(); + unloadCommand.End(); + } + + private UnloadFilteredDomainObjectsCommand CreateUnloadCommand (Predicate domainObjectFilter) + { + var invalidDomainObjectManager = (IInvalidDomainObjectManager)PrivateInvoke.GetNonPublicField(TestableClientTransaction.DataManager, "_invalidDomainObjectManager"); + Assertion.IsNotNull(invalidDomainObjectManager, "DataManager._invalidDomainObjectManager not found or null"); + + return new UnloadFilteredDomainObjectsCommand( + (DataContainerMap)TestableClientTransaction.DataManager.DataContainers, + invalidDomainObjectManager, + (RelationEndPointMap)TestableClientTransaction.DataManager.RelationEndPoints, + TestableClientTransaction.DataManager.TransactionEventSink, + domainObjectFilter); + } + + } +} diff --git a/Remotion/Data/DomainObjects.UnitTests/TestableClientTransaction.cs b/Remotion/Data/DomainObjects.UnitTests/TestableClientTransaction.cs index 7486e901ba..7a543bed45 100644 --- a/Remotion/Data/DomainObjects.UnitTests/TestableClientTransaction.cs +++ b/Remotion/Data/DomainObjects.UnitTests/TestableClientTransaction.cs @@ -37,7 +37,7 @@ protected TestableClientTransaction (IClientTransactionComponentFactory componen public IClientTransactionEventBroker EventBroker { - get { return (IClientTransactionEventBroker)PrivateInvoke.GetNonPublicProperty(this, typeof(ClientTransaction), "eventBroker"); } + get { return (IClientTransactionEventBroker)PrivateInvoke.GetNonPublicProperty(this, typeof(ClientTransaction), "_eventBroker"); } } public new DomainObject GetObject (ObjectID id, bool includeDeleted) diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index 23edcc0e91..54d90a3953 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -28,11 +28,9 @@ namespace Remotion.Data.DomainObjects.DataManagement.Commands { public class UnloadFilteredDomainObjectsCommand : IDataManagementCommand { - public IRelationEndPointManager RelationEndPointManager { get; } public DataContainerMap DataContainerMap { get; } public IInvalidDomainObjectManager InvalidDomainObjectManager { get; } public RelationEndPointMap RelationEndPointMap { get; } - public IRelationEndPointRegistrationAgent RelationEndPointRegistrationAgent { get; } public IClientTransactionEventSink TransactionEventSink { get; } public Predicate DomainObjectFilter { get; } private IReadOnlyCollection _dataContainersForUnloading = Array.Empty(); @@ -41,27 +39,21 @@ public class UnloadFilteredDomainObjectsCommand : IDataManagementCommand private List _exceptions = new List(); public UnloadFilteredDomainObjectsCommand ( - IRelationEndPointManager relationEndPointManager, DataContainerMap dataContainerMap, IInvalidDomainObjectManager invalidDomainObjectManager, - RelationEndPointMap relationRelationEndPointMap, - IRelationEndPointRegistrationAgent relationEndPointRegistrationAgent, + RelationEndPointMap relationEndPointMap, IClientTransactionEventSink transactionEventSink, Predicate domainObjectFilter) { - ArgumentUtility.CheckNotNull("relationEndPointManager", relationEndPointManager); ArgumentUtility.CheckNotNull("dataContainerMap", dataContainerMap); ArgumentUtility.CheckNotNull("invalidDomainObjectManager", invalidDomainObjectManager); - ArgumentUtility.CheckNotNull("relationRelationEndPointMap", relationRelationEndPointMap); - ArgumentUtility.CheckNotNull("relationEndPointRegistrationAgent", relationEndPointRegistrationAgent); + ArgumentUtility.CheckNotNull("relationEndPointMap", relationEndPointMap); ArgumentUtility.CheckNotNull("transactionEventSink", transactionEventSink); ArgumentUtility.CheckNotNull("domainObjectFilter", domainObjectFilter); - RelationEndPointManager = relationEndPointManager; DataContainerMap = dataContainerMap; InvalidDomainObjectManager = invalidDomainObjectManager; - RelationEndPointMap = relationRelationEndPointMap; - RelationEndPointRegistrationAgent = relationEndPointRegistrationAgent; + RelationEndPointMap = relationEndPointMap; TransactionEventSink = transactionEventSink; DomainObjectFilter = domainObjectFilter; } @@ -103,16 +95,18 @@ public void Perform () foreach (var relationEndPoint in _realObjectEndPointsForUnloading) { + relationEndPoint.Rollback(); RelationEndPointMap.RemoveEndPoint(relationEndPoint.ID); - RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); + // RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); } foreach (var relationEndPoint in _virtualEndPointsForUnloading) { + relationEndPoint.Rollback(); RelationEndPointMap.RemoveEndPoint(relationEndPoint.ID); - relationEndPoint.MarkDataIncomplete(); - Assertion.IsTrue(relationEndPoint.CanBeCollected, "VirtualEndPoint '{0}' cannot be collected.", relationEndPoint.ID); - RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); + //relationEndPoint.MarkDataIncomplete(); + // Assertion.IsTrue(relationEndPoint.CanBeCollected, "VirtualEndPoint '{0}' cannot be collected.", relationEndPoint.ID); + // RelationEndPointRegistrationAgent.UnregisterEndPoint(relationEndPoint, RelationEndPointMap); } foreach (var dataContainer in _dataContainersForUnloading) @@ -153,7 +147,7 @@ public ExpandedCommand ExpandToAllRelatedObjects () var virtualEndPointsNotPartOfUnloadSet = new HashSet(); var objectIDs = dataContainers.ToDictionary(dc => dc.ID); - var endPoints = RelationEndPointManager.RelationEndPoints + var endPoints = RelationEndPointMap .ApplySideEffect(ep => Assertion.IsFalse(ep.IsNull, "RelationEndPoint '{0}' is a null end-point.", ep.ID)) .Where(ep => ep.ObjectID != null); @@ -171,10 +165,21 @@ public ExpandedCommand ExpandToAllRelatedObjects () { realObjectEndPoints.Add(realObjectEndPoint); } - else if ((realObjectEndPoint.OppositeObjectID == null || objectIDs.ContainsKey(realObjectEndPoint.OppositeObjectID)) - && (realObjectEndPoint.OriginalOppositeObjectID == null || objectIDs.ContainsKey(realObjectEndPoint.OriginalOppositeObjectID))) + else if (IsPartOfUnloadedSet(realObjectEndPoint.OppositeObjectID) && IsPartOfUnloadedSet(realObjectEndPoint.OriginalOppositeObjectID)) { realObjectEndPoints.Add(realObjectEndPoint); + var virtualEndPointID = realObjectEndPoint.GetOppositeRelationEndPointID(); + if (virtualEndPointID != null) + { + var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointMap[virtualEndPointID]; + if (virtualEndPoint != null) + { + if (virtualEndPoint.HasChanged) + virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + else + virtualObjectEndPoints.Add(virtualEndPoint); + } + } } else { @@ -182,9 +187,12 @@ public ExpandedCommand ExpandToAllRelatedObjects () var virtualEndPointID = realObjectEndPoint.GetOppositeRelationEndPointID(); if (virtualEndPointID != null) { - var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(virtualEndPointID); + var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointMap[virtualEndPointID]; if (virtualEndPoint != null) - virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + { + if (virtualEndPoint.HasChanged) + virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); + } } } } @@ -202,11 +210,14 @@ public ExpandedCommand ExpandToAllRelatedObjects () { virtualObjectEndPoints.Add(virtualEndPoint); } + else if (virtualEndPoint.HasChanged == false) + { + virtualObjectEndPoints.Add(virtualEndPoint); + } else if (virtualEndPoint.Definition.Cardinality == CardinalityType.One) { var virtualObjectEndPoint = (IVirtualObjectEndPoint)virtualEndPoint; - if ((virtualObjectEndPoint.OppositeObjectID == null || objectIDs.ContainsKey(virtualObjectEndPoint.OppositeObjectID)) - && (virtualObjectEndPoint.OriginalOppositeObjectID == null || objectIDs.ContainsKey(virtualObjectEndPoint.OriginalOppositeObjectID))) + if (IsPartOfUnloadedSet(virtualObjectEndPoint.OppositeObjectID) && IsPartOfUnloadedSet(virtualObjectEndPoint.OriginalOppositeObjectID)) { virtualObjectEndPoints.Add(virtualEndPoint); } @@ -216,7 +227,7 @@ public ExpandedCommand ExpandToAllRelatedObjects () var realObjectEndPointID = virtualObjectEndPoint.GetOppositeRelationEndPointID(); if (realObjectEndPointID != null) { - var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(realObjectEndPointID); + var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointMap[realObjectEndPointID]; if (oppositeRealObjectEndPoint != null) realObjectEndPointsNotPartOfUnloadSet.Add(oppositeRealObjectEndPoint); } @@ -233,10 +244,10 @@ public ExpandedCommand ExpandToAllRelatedObjects () else { virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); - var realObjectEndPointIDs = collectionEndPoint.GetOppositeRelationEndPointIDs().Where(epID => epID.ObjectID != null && !objectIDs.ContainsKey(epID.ObjectID)); + var realObjectEndPointIDs = collectionEndPoint.GetOppositeRelationEndPointIDs().Where(epID =>!IsPartOfUnloadedSet(epID.ObjectID)); foreach (var realObjectEndPointID in realObjectEndPointIDs) { - var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointManager.GetRelationEndPointWithoutLoading(realObjectEndPointID); + var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointMap[realObjectEndPointID]; if (oppositeRealObjectEndPoint != null) realObjectEndPointsNotPartOfUnloadSet.Add(oppositeRealObjectEndPoint); } @@ -246,12 +257,17 @@ public ExpandedCommand ExpandToAllRelatedObjects () } } + realObjectEndPointsNotPartOfUnloadSet.RemoveWhere(realObjectEndPoints.Contains); + virtualEndPointsNotPartOfUnloadSet.RemoveWhere(virtualObjectEndPoints.Contains); + return ( RealObjectEndPoints: realObjectEndPoints, VirtualEndPoints: virtualObjectEndPoints, RealObjectEndPointsNotPartOfUnloadSet: realObjectEndPointsNotPartOfUnloadSet, VirtualEndPointsNotPartOfUnloadSet: virtualEndPointsNotPartOfUnloadSet ); + + bool IsPartOfUnloadedSet (ObjectID? objectID) => objectID == null || objectIDs.ContainsKey(objectID) || DataContainerMap[objectID] == null; } } } From 1d63937e3bb2d46c7f4404032c93b5fee097c639 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 09:21:06 +0100 Subject: [PATCH 06/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 777c7a8277..4ad15afca5 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -88,6 +88,27 @@ public void UnloadAllObjects_WithDomainObjectCollectionRelation () Assert.That(orderItem.State.IsNotLoadedYet, Is.True); } + [Test] + public void UnloadAllObjects_WithVirtualCollectionRelation () + { + var product = (Product)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Product1, false); + product.Reviews.EnsureDataComplete(); + + var productReview = product.Reviews.First(); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(product.State.IsNotLoadedYet, Is.True); + Assert.That(product.Reviews.IsDataComplete, Is.False); + Assert.That(productReview.State.IsNotLoadedYet, Is.True); + } + private void UnloadFiltered (Predicate domainObjectFilter) { From a0e0a513bf0c0eb166fde72660570dc27fe9b7eb Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 10:09:18 +0100 Subject: [PATCH 07/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 4ad15afca5..41e878358f 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -24,6 +24,7 @@ using Remotion.Data.DomainObjects.Infrastructure.InvalidObjects; using Remotion.Data.DomainObjects.UnitTests.TestDomain; using Remotion.Development.UnitTesting; +using Remotion.TypePipe; using Remotion.Utilities; namespace Remotion.Data.DomainObjects.UnitTests.IntegrationTests.Unload @@ -32,7 +33,7 @@ namespace Remotion.Data.DomainObjects.UnitTests.IntegrationTests.Unload public class UnloadFilteredTest: ClientTransactionBaseTest { [Test] - public void UnloadAllObjects_SingleDomainObject () + public void UnloadFiltered_WithAllObjects_SingleDomainObject_WithoutChanges () { var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); @@ -48,7 +49,7 @@ public void UnloadAllObjects_SingleDomainObject () } [Test] - public void UnloadAllObjects_WithOneToOneRelation () + public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithoutChanges () { var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); order.OrderTicket.EnsureDataAvailable(); @@ -68,7 +69,30 @@ public void UnloadAllObjects_WithOneToOneRelation () } [Test] - public void UnloadAllObjects_WithDomainObjectCollectionRelation () + public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges () + { + var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + order.OrderTicket.EnsureDataAvailable(); + + var orderTicket1 = order.OrderTicket; + var orderTicket2 = (OrderTicket)LifetimeService.NewObject(TestableClientTransaction, typeof(OrderTicket), ParamList.Empty); + orderTicket2.Order = order; + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(order.State.IsNotLoadedYet, Is.True); + Assert.That(orderTicket1.State.IsNotLoadedYet, Is.True); + Assert.That(orderTicket2.State.IsInvalid, Is.True); + } + + [Test] + public void UnloadFiltered_WithAllObjects_WithDomainObjectCollectionRelation_WithoutChanges () { var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); order.OrderItems.EnsureDataComplete(); @@ -89,12 +113,56 @@ public void UnloadAllObjects_WithDomainObjectCollectionRelation () } [Test] - public void UnloadAllObjects_WithVirtualCollectionRelation () + public void UnloadFiltered_WithAllObjects_WithDomainObjectCollectionRelation_WithChanges () + { + var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + order.OrderItems.EnsureDataComplete(); + + var orderItem = order.OrderItems.First(); + orderItem.Order = null; + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(order.State.IsNotLoadedYet, Is.True); + Assert.That(order.OrderItems.IsDataComplete, Is.False); + Assert.That(orderItem.State.IsNotLoadedYet, Is.True); + } + + [Test] + public void UnloadFiltered_WithAllObjects_WithVirtualCollectionRelation_WithoutChanges () + { + var product = (Product)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Product1, false); + product.Reviews.EnsureDataComplete(); + + var productReview = product.Reviews.First(); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(product.State.IsNotLoadedYet, Is.True); + Assert.That(product.Reviews.IsDataComplete, Is.False); + Assert.That(productReview.State.IsNotLoadedYet, Is.True); + } + + [Test] + public void UnloadFiltered_WithAllObjects_WithVirtualCollectionRelation_WithChanges () { var product = (Product)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Product1, false); product.Reviews.EnsureDataComplete(); var productReview = product.Reviews.First(); + productReview.Product = null; Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); From 22a93bf412a1a2170faae39369cfc4b53b700837 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 10:48:13 +0100 Subject: [PATCH 08/15] RM-8240 - temp --- .../DataManagement/DataManagerTest.cs | 16 ++++++++++++++++ .../DelegatingDataManagerTest.cs | 4 +++- .../SerializableDataManagerFake.cs | 5 +++++ .../UnloadFilteredDomainObjectsCommantTest.cs | 19 +------------------ .../DataManagement/DataManager.cs | 13 +++++++++++++ .../DataManagement/DelegatingDataManager.cs | 5 +++++ .../DataManagement/IDataManager.cs | 1 + .../DomainImplementation/UnloadService.cs | 10 ++++++++++ 8 files changed, 54 insertions(+), 19 deletions(-) diff --git a/Remotion/Data/DomainObjects.UnitTests/DataManagement/DataManagerTest.cs b/Remotion/Data/DomainObjects.UnitTests/DataManagement/DataManagerTest.cs index 12a883f872..52b627fa5f 100644 --- a/Remotion/Data/DomainObjects.UnitTests/DataManagement/DataManagerTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/DataManagement/DataManagerTest.cs @@ -744,6 +744,22 @@ public void CreateUnloadAllCommand () Assert.That(unloadAllCommand.TransactionEventSink, Is.SameAs(_transactionEventSinkStub.Object)); } + [Test] + [Ignore("TODO RM-8240: enable after hard cast has been removed")] + public void CreateUnloadFilteredDomainObjectsCommand () + { + Predicate domainObjectFilter = obj => true; + var command = _dataManagerWithMocks.CreateUnloadFilteredDomainObjectsCommand(domainObjectFilter); + + Assert.That(command, Is.TypeOf()); + var unloadFilteredDomainObjectsCommand = (UnloadFilteredDomainObjectsCommand)command; + Assert.That(unloadFilteredDomainObjectsCommand.DataContainerMap, Is.SameAs(DataManagerTestHelper.GetDataContainerMap(_dataManagerWithMocks))); + Assert.That(unloadFilteredDomainObjectsCommand.RelationEndPointMap, Is.SameAs(DataManagerTestHelper.GetRelationEndPointManager(_dataManagerWithMocks))); + Assert.That(unloadFilteredDomainObjectsCommand.InvalidDomainObjectManager, Is.SameAs(DataManagerTestHelper.GetInvalidDomainObjectManager(_dataManagerWithMocks))); + Assert.That(unloadFilteredDomainObjectsCommand.TransactionEventSink, Is.SameAs(_transactionEventSinkStub.Object)); + Assert.That(unloadFilteredDomainObjectsCommand.DomainObjectFilter, Is.SameAs(domainObjectFilter)); + } + [Test] public void GetDataContainerWithoutLoading_NotLoaded () { diff --git a/Remotion/Data/DomainObjects.UnitTests/DataManagement/DelegatingDataManagerTest.cs b/Remotion/Data/DomainObjects.UnitTests/DataManagement/DelegatingDataManagerTest.cs index 20f6877eb7..701f8691a5 100644 --- a/Remotion/Data/DomainObjects.UnitTests/DataManagement/DelegatingDataManagerTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/DataManagement/DelegatingDataManagerTest.cs @@ -45,6 +45,8 @@ public void DelegatingMembers () new IRelationEndPoint[0]); var dataManagementCommand = new Mock(); var randomBoolean = BooleanObjectMother.GetRandomBoolean(); + Predicate expectedPredicate = state => state.IsUnchanged; + Predicate domainObjectFilter = obj => true; CheckDelegation(dm => dm.GetOrCreateVirtualEndPoint(relationEndPointID), virtualEndPoint.Object); CheckDelegation(dm => dm.GetRelationEndPointWithLazyLoad(relationEndPointID), virtualEndPoint.Object); @@ -57,7 +59,6 @@ public void DelegatingMembers () CheckDelegation(dm => dm.GetState(objectID), new DomainObjectState.Builder().SetDeleted().Value); CheckDelegation(dm => dm.GetDataContainerWithLazyLoad(objectID, randomBoolean), dataContainer); CheckDelegation(dm => dm.GetDataContainersWithLazyLoad(new[] { objectID }, true), new[] { dataContainer }); - Predicate expectedPredicate = state => state.IsUnchanged; CheckDelegation(dm => dm.GetLoadedDataByObjectState(expectedPredicate), new[] { persistableData }); CheckDelegation(dm => dm.MarkInvalid(domainObject)); CheckDelegation(dm => dm.MarkNotInvalid(objectID)); @@ -67,6 +68,7 @@ public void DelegatingMembers () CheckDelegation(dm => dm.CreateUnloadCommand(new[] { objectID }), dataManagementCommand.Object); CheckDelegation(dm => dm.CreateUnloadVirtualEndPointsCommand(new[] { relationEndPointID }), dataManagementCommand.Object); CheckDelegation(dm => dm.CreateUnloadAllCommand(), dataManagementCommand.Object); + CheckDelegation(dm => dm.CreateUnloadFilteredDomainObjectsCommand(domainObjectFilter), dataManagementCommand.Object); CheckDelegation(dm => dm.LoadLazyCollectionEndPoint(relationEndPointID)); CheckDelegation(dm => dm.LoadLazyVirtualObjectEndPoint(relationEndPointID)); CheckDelegation(dm => dm.LoadLazyDataContainer(objectID), dataContainer); diff --git a/Remotion/Data/DomainObjects.UnitTests/DataManagement/SerializableFakes/SerializableDataManagerFake.cs b/Remotion/Data/DomainObjects.UnitTests/DataManagement/SerializableFakes/SerializableDataManagerFake.cs index 5428d7c0d6..6dfbe6232c 100644 --- a/Remotion/Data/DomainObjects.UnitTests/DataManagement/SerializableFakes/SerializableDataManagerFake.cs +++ b/Remotion/Data/DomainObjects.UnitTests/DataManagement/SerializableFakes/SerializableDataManagerFake.cs @@ -139,5 +139,10 @@ public IDataManagementCommand CreateUnloadAllCommand () { throw new NotImplementedException(); } + + public IDataManagementCommand CreateUnloadFilteredDomainObjectsCommand (Predicate domainObjectFilter) + { + throw new NotImplementedException(); + } } } diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 41e878358f..636c26f123 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -180,24 +180,7 @@ public void UnloadFiltered_WithAllObjects_WithVirtualCollectionRelation_WithChan private void UnloadFiltered (Predicate domainObjectFilter) { - var unloadCommand = CreateUnloadCommand(domainObjectFilter); - unloadCommand.Begin(); - unloadCommand.Perform(); - unloadCommand.End(); + UnloadService.UnloadFiltered(TestableClientTransaction, domainObjectFilter); } - - private UnloadFilteredDomainObjectsCommand CreateUnloadCommand (Predicate domainObjectFilter) - { - var invalidDomainObjectManager = (IInvalidDomainObjectManager)PrivateInvoke.GetNonPublicField(TestableClientTransaction.DataManager, "_invalidDomainObjectManager"); - Assertion.IsNotNull(invalidDomainObjectManager, "DataManager._invalidDomainObjectManager not found or null"); - - return new UnloadFilteredDomainObjectsCommand( - (DataContainerMap)TestableClientTransaction.DataManager.DataContainers, - invalidDomainObjectManager, - (RelationEndPointMap)TestableClientTransaction.DataManager.RelationEndPoints, - TestableClientTransaction.DataManager.TransactionEventSink, - domainObjectFilter); - } - } } diff --git a/Remotion/Data/DomainObjects/DataManagement/DataManager.cs b/Remotion/Data/DomainObjects/DataManagement/DataManager.cs index f562c587f9..80d8a49ca7 100644 --- a/Remotion/Data/DomainObjects/DataManagement/DataManager.cs +++ b/Remotion/Data/DomainObjects/DataManagement/DataManager.cs @@ -444,6 +444,19 @@ public IDataManagementCommand CreateUnloadAllCommand () return new UnloadAllCommand(_relationEndPointManager, _dataContainerMap, _invalidDomainObjectManager, _transactionEventSink); } + public IDataManagementCommand CreateUnloadFilteredDomainObjectsCommand (Predicate domainObjectFilter) + { + ArgumentUtility.CheckNotNull("domainObjectFilter", domainObjectFilter); + + return new UnloadFilteredDomainObjectsCommand( + _dataContainerMap, + _invalidDomainObjectManager, + //TODO RM-8240: refactor UnloadFilteredDomainObjectsCommand to move relation endpoint unloading to separate command created by RelationEndPointManager + (RelationEndPointMap)_relationEndPointManager.RelationEndPoints, + _transactionEventSink, + domainObjectFilter); + } + private ClientTransactionsDifferException CreateClientTransactionsDifferException (string message, params object?[] args) { return new ClientTransactionsDifferException(String.Format(message, args)); diff --git a/Remotion/Data/DomainObjects/DataManagement/DelegatingDataManager.cs b/Remotion/Data/DomainObjects/DataManagement/DelegatingDataManager.cs index 4de9fd561f..bed54411e8 100644 --- a/Remotion/Data/DomainObjects/DataManagement/DelegatingDataManager.cs +++ b/Remotion/Data/DomainObjects/DataManagement/DelegatingDataManager.cs @@ -136,6 +136,11 @@ public IDataManagementCommand CreateUnloadAllCommand () return SafeInnerDataManager.CreateUnloadAllCommand(); } + public IDataManagementCommand CreateUnloadFilteredDomainObjectsCommand (Predicate domainObjectFilter) + { + return SafeInnerDataManager.CreateUnloadFilteredDomainObjectsCommand(domainObjectFilter); + } + public void LoadLazyCollectionEndPoint (RelationEndPointID endPointID) { SafeInnerDataManager.LoadLazyCollectionEndPoint(endPointID); diff --git a/Remotion/Data/DomainObjects/DataManagement/IDataManager.cs b/Remotion/Data/DomainObjects/DataManagement/IDataManager.cs index 436fdca023..d385c50f59 100644 --- a/Remotion/Data/DomainObjects/DataManagement/IDataManager.cs +++ b/Remotion/Data/DomainObjects/DataManagement/IDataManager.cs @@ -49,5 +49,6 @@ public interface IDataManager : IRelationEndPointProvider, ILoadedDataContainerP IDataManagementCommand CreateUnloadCommand (params ObjectID[] objectIDs); IDataManagementCommand CreateUnloadVirtualEndPointsCommand (params RelationEndPointID[] endPointIDs); IDataManagementCommand CreateUnloadAllCommand (); + IDataManagementCommand CreateUnloadFilteredDomainObjectsCommand (Predicate domainObjectFilter); } } diff --git a/Remotion/Data/DomainObjects/DomainImplementation/UnloadService.cs b/Remotion/Data/DomainObjects/DomainImplementation/UnloadService.cs index f6e3f1130c..6420a23c7e 100644 --- a/Remotion/Data/DomainObjects/DomainImplementation/UnloadService.cs +++ b/Remotion/Data/DomainObjects/DomainImplementation/UnloadService.cs @@ -280,6 +280,16 @@ public static void UnloadAll (ClientTransaction clientTransaction) executor.ExecuteCommandForTransactionHierarchy(clientTransaction); } + public static void UnloadFiltered (ClientTransaction clientTransaction, Predicate domainObjectFilter) + { + ArgumentUtility.CheckNotNull("clientTransaction", clientTransaction); + ArgumentUtility.CheckNotNull("domainObjectFilter", domainObjectFilter); + + Func commandFactory = tx => tx.DataManager.CreateUnloadFilteredDomainObjectsCommand(domainObjectFilter); + var executor = new TransactionHierarchyCommandExecutor(commandFactory); + executor.ExecuteCommandForTransactionHierarchy(clientTransaction); + } + private static void CheckVirtualEndPointID (RelationEndPointID endPointID) { if (!endPointID.Definition.IsVirtual) From 176d87c9991d823a84ba29f671511397995b7b5e Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 10:55:36 +0100 Subject: [PATCH 09/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 636c26f123..904713635c 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -17,15 +17,9 @@ using System; using System.Linq; using NUnit.Framework; -using Remotion.Data.DomainObjects.DataManagement; -using Remotion.Data.DomainObjects.DataManagement.Commands; -using Remotion.Data.DomainObjects.DataManagement.RelationEndPoints; using Remotion.Data.DomainObjects.DomainImplementation; -using Remotion.Data.DomainObjects.Infrastructure.InvalidObjects; using Remotion.Data.DomainObjects.UnitTests.TestDomain; -using Remotion.Development.UnitTesting; using Remotion.TypePipe; -using Remotion.Utilities; namespace Remotion.Data.DomainObjects.UnitTests.IntegrationTests.Unload { @@ -87,8 +81,11 @@ public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges () Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); Assert.That(order.State.IsNotLoadedYet, Is.True); + Assert.That(order.State.IsRelationChanged, Is.False); Assert.That(orderTicket1.State.IsNotLoadedYet, Is.True); Assert.That(orderTicket2.State.IsInvalid, Is.True); + Assert.That(orderTicket1.State.IsRelationChanged, Is.False); + Assert.That(orderTicket2.State.IsRelationChanged, Is.False); } [Test] @@ -131,7 +128,9 @@ public void UnloadFiltered_WithAllObjects_WithDomainObjectCollectionRelation_Wit Assert.That(order.State.IsNotLoadedYet, Is.True); Assert.That(order.OrderItems.IsDataComplete, Is.False); + Assert.That(order.State.IsRelationChanged, Is.False); Assert.That(orderItem.State.IsNotLoadedYet, Is.True); + Assert.That(orderItem.State.IsRelationChanged, Is.False); } [Test] @@ -174,7 +173,53 @@ public void UnloadFiltered_WithAllObjects_WithVirtualCollectionRelation_WithChan Assert.That(product.State.IsNotLoadedYet, Is.True); Assert.That(product.Reviews.IsDataComplete, Is.False); + Assert.That(product.State.IsRelationChanged, Is.False); Assert.That(productReview.State.IsNotLoadedYet, Is.True); + Assert.That(productReview.State.IsRelationChanged, Is.False); + } + + [Test] + public void UnloadFiltered_WithAllObjects_WithAnonymousRelation_WithoutChanges () + { + var location = (Location)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Location1, false); + + var client = location.Client; + client.EnsureDataAvailable(); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(location.State.IsNotLoadedYet, Is.True); + Assert.That(client.State.IsNotLoadedYet, Is.True); + } + + [Test] + public void UnloadFiltered_WithAllObjects_WithAnonymousRelation_WithChanges () + { + var location = (Location)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Location1, false); + + var client = location.Client; + client.EnsureDataAvailable(); + location.Client = null; + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + UnloadFiltered(obj => true); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); + + Assert.That(location.State.IsNotLoadedYet, Is.True); + Assert.That(location.State.IsRelationChanged, Is.False); + Assert.That(client.State.IsNotLoadedYet, Is.True); + Assert.That(client.State.IsRelationChanged, Is.False); + } From 62db2edf6ac610981bf8c51624557ec040e5fe15 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 12:54:16 +0100 Subject: [PATCH 10/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 25 +++++++++++++++ .../UnloadFilteredDomainObjectsCommand.cs | 31 +++++++++++++------ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 904713635c..3084b749c5 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -219,7 +219,32 @@ public void UnloadFiltered_WithAllObjects_WithAnonymousRelation_WithChanges () Assert.That(location.State.IsRelationChanged, Is.False); Assert.That(client.State.IsNotLoadedYet, Is.True); Assert.That(client.State.IsRelationChanged, Is.False); + } + + [Test] + public void UnloadFiltered_WithSomeObjects_WithAnonymousRelation_WithChanges_IncludeOnlyForeignKeySide () + { + var location = (Location)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Location1, false); + var client = location.Client; + client.EnsureDataAvailable(); + location.Client = null; + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + var locationEndPoints = TestableClientTransaction.DataManager.RelationEndPoints.Where(ep => ep.Definition.ClassDefinition.ClassType == typeof(Location)).ToArray(); + + UnloadFiltered(obj => obj == location); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.DataContainers.Select(dc => dc.DomainObject), Has.No.AnyOf(location)); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints.Select(ep => ep.Definition), Has.No.AnyOf(locationEndPoints)); + + Assert.That(location.State.IsNotLoadedYet, Is.True); + Assert.That(location.State.IsRelationChanged, Is.False); + Assert.That(client.State.IsUnchanged, Is.True); + Assert.That(client.State.IsRelationChanged, Is.False); } diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index 54d90a3953..2d7cd408d9 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -73,7 +73,9 @@ public void Begin () var relationEndPoints = GetRelationEndPointsForUnloading(_dataContainersForUnloading); if (relationEndPoints.RealObjectEndPointsNotPartOfUnloadSet.Any() || relationEndPoints.VirtualEndPointsNotPartOfUnloadSet.Any()) { - _exceptions.Add(new InvalidOperationException("Transitive closure violated")); + var problematicEndPoints = relationEndPoints.RealObjectEndPointsNotPartOfUnloadSet.Select(ep => ep.ID) + .Concat(relationEndPoints.VirtualEndPointsNotPartOfUnloadSet.Select(ep => ep.ID)); + _exceptions.Add(new InvalidOperationException("Transitive closure violated:\r\n" + string.Join(",\r\n", problematicEndPoints))); throw _exceptions.First(); } else @@ -161,16 +163,29 @@ public ExpandedCommand ExpandToAllRelatedObjects () { if (isPartOfUnloadedSet) { + var virtualEndPointDefinition = realObjectEndPoint.ID.Definition.RelationDefinition.GetOppositeEndPointDefinition(realObjectEndPoint.ID.Definition); + Assertion.IsNotNull(virtualEndPointDefinition, "Inconsistent relation definition for endpoint '{0}'.", realObjectEndPoint.ID.Definition); + if (realObjectEndPoint.IsNull) { realObjectEndPoints.Add(realObjectEndPoint); } - else if (IsPartOfUnloadedSet(realObjectEndPoint.OppositeObjectID) && IsPartOfUnloadedSet(realObjectEndPoint.OriginalOppositeObjectID)) + else if (virtualEndPointDefinition.IsAnonymous) { realObjectEndPoints.Add(realObjectEndPoint); + } + else + { var virtualEndPointID = realObjectEndPoint.GetOppositeRelationEndPointID(); - if (virtualEndPointID != null) + Assertion.IsNotNull( + virtualEndPointID, + "GetOppositeRelationEndPointID() for real endpoint '{0}' evaluated and returned null but virtual endpoint is not anonymous.", + realObjectEndPoint.ID); + + if (IsPartOfUnloadedSet(realObjectEndPoint.OppositeObjectID) && IsPartOfUnloadedSet(realObjectEndPoint.OriginalOppositeObjectID)) { + realObjectEndPoints.Add(realObjectEndPoint); + var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointMap[virtualEndPointID]; if (virtualEndPoint != null) { @@ -180,13 +195,9 @@ public ExpandedCommand ExpandToAllRelatedObjects () virtualObjectEndPoints.Add(virtualEndPoint); } } - } - else - { - realObjectEndPointsNotPartOfUnloadSet.Add(realObjectEndPoint); - var virtualEndPointID = realObjectEndPoint.GetOppositeRelationEndPointID(); - if (virtualEndPointID != null) + else { + realObjectEndPointsNotPartOfUnloadSet.Add(realObjectEndPoint); var virtualEndPoint = (IVirtualEndPoint?)RelationEndPointMap[virtualEndPointID]; if (virtualEndPoint != null) { @@ -244,7 +255,7 @@ public ExpandedCommand ExpandToAllRelatedObjects () else { virtualEndPointsNotPartOfUnloadSet.Add(virtualEndPoint); - var realObjectEndPointIDs = collectionEndPoint.GetOppositeRelationEndPointIDs().Where(epID =>!IsPartOfUnloadedSet(epID.ObjectID)); + var realObjectEndPointIDs = collectionEndPoint.GetOppositeRelationEndPointIDs().Where(epID => !IsPartOfUnloadedSet(epID.ObjectID)); foreach (var realObjectEndPointID in realObjectEndPointIDs) { var oppositeRealObjectEndPoint = (IRealObjectEndPoint?)RelationEndPointMap[realObjectEndPointID]; From 65a1605c9c32cf0ec2685f6f28a449ada65cfe28 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 14:57:13 +0100 Subject: [PATCH 11/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 3084b749c5..aa4a383d39 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -230,6 +230,11 @@ public void UnloadFiltered_WithSomeObjects_WithAnonymousRelation_WithChanges_Inc client.EnsureDataAvailable(); location.Client = null; + Assert.That(location.State.IsChanged, Is.True); + Assert.That(location.State.IsRelationChanged, Is.True); + Assert.That(client.State.IsUnchanged, Is.True); + Assert.That(client.State.IsRelationChanged, Is.False); + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); var locationEndPoints = TestableClientTransaction.DataManager.RelationEndPoints.Where(ep => ep.Definition.ClassDefinition.ClassType == typeof(Location)).ToArray(); @@ -245,6 +250,47 @@ public void UnloadFiltered_WithSomeObjects_WithAnonymousRelation_WithChanges_Inc Assert.That(location.State.IsRelationChanged, Is.False); Assert.That(client.State.IsUnchanged, Is.True); Assert.That(client.State.IsRelationChanged, Is.False); + + location.EnsureDataAvailable(); + Assert.That(location.Client, Is.SameAs(client)); + } + + [Test] + public void UnloadFiltered_WithSomeObjects_WithAnonymousRelation_WithChanges_IncludeOnlyAnonymousSide () + { + var location = (Location)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Location1, false); + + var client = location.Client; + client.EnsureDataAvailable(); + location.Client = null; + + Assert.That(location.State.IsChanged, Is.True); + Assert.That(location.State.IsDataChanged, Is.True); + Assert.That(location.State.IsRelationChanged, Is.True); + Assert.That(client.State.IsUnchanged, Is.True); + Assert.That(client.State.IsRelationChanged, Is.False); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + var clientEndPoints = TestableClientTransaction.DataManager.RelationEndPoints.Where(ep => ep.Definition.ClassDefinition.ClassType == typeof(Client)).ToArray(); + + UnloadFiltered(obj => obj == client); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.DataContainers.Select(dc => dc.DomainObject), Has.No.AnyOf(client)); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints.Select(ep => ep.Definition), Has.No.AnyOf(clientEndPoints)); + + Assert.That(location.State.IsChanged, Is.True); + Assert.That(location.State.IsDataChanged, Is.True); + Assert.That(location.State.IsRelationChanged, Is.True); + Assert.That(client.State.IsNotLoadedYet, Is.True); + Assert.That(client.State.IsRelationChanged, Is.False); + + var client2 = (Client)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Client2, false); + Assert.That(client2.ID, Is.Not.EqualTo(client.ID)); + location.Client = client2; + Assert.That(location.Client, Is.EqualTo(client2)); } From 2a4fe3dd1f18bea14af01781daea0b25952145d2 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 15:31:55 +0100 Subject: [PATCH 12/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index aa4a383d39..4c390aba9e 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -65,12 +65,12 @@ public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithoutChanges () [Test] public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges () { - var order = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); - order.OrderTicket.EnsureDataAvailable(); + var order1 = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + var order2 = (Order)LifetimeService.NewObject(TestableClientTransaction,typeof(Order), ParamList.Empty); + order1.OrderTicket.EnsureDataAvailable(); - var orderTicket1 = order.OrderTicket; - var orderTicket2 = (OrderTicket)LifetimeService.NewObject(TestableClientTransaction, typeof(OrderTicket), ParamList.Empty); - orderTicket2.Order = order; + var orderTicket = order1.OrderTicket; + orderTicket.Order = order2; Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); @@ -80,12 +80,12 @@ public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges () Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Empty); Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Empty); - Assert.That(order.State.IsNotLoadedYet, Is.True); - Assert.That(order.State.IsRelationChanged, Is.False); - Assert.That(orderTicket1.State.IsNotLoadedYet, Is.True); - Assert.That(orderTicket2.State.IsInvalid, Is.True); - Assert.That(orderTicket1.State.IsRelationChanged, Is.False); - Assert.That(orderTicket2.State.IsRelationChanged, Is.False); + Assert.That(order1.State.IsNotLoadedYet, Is.True); + Assert.That(order1.State.IsRelationChanged, Is.False); + Assert.That(order2.State.IsInvalid, Is.True); + Assert.That(order2.State.IsRelationChanged, Is.False); + Assert.That(orderTicket.State.IsNotLoadedYet, Is.True); + Assert.That(orderTicket.State.IsRelationChanged, Is.False); } [Test] From 3b92b64f7dcec9d071b1c23a0b271cccc064c8f2 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 15:50:48 +0100 Subject: [PATCH 13/15] RM-8240 - temp --- .../Commands/UnloadFilteredDomainObjectsCommand.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs index 2d7cd408d9..a6345e29fb 100644 --- a/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs +++ b/Remotion/Data/DomainObjects/DataManagement/Commands/UnloadFilteredDomainObjectsCommand.cs @@ -75,7 +75,10 @@ public void Begin () { var problematicEndPoints = relationEndPoints.RealObjectEndPointsNotPartOfUnloadSet.Select(ep => ep.ID) .Concat(relationEndPoints.VirtualEndPointsNotPartOfUnloadSet.Select(ep => ep.ID)); - _exceptions.Add(new InvalidOperationException("Transitive closure violated:\r\n" + string.Join(",\r\n", problematicEndPoints))); + _exceptions.Add( + new InvalidOperationException( + "Cannot unload the following relation endpoints because the associated domain objects have not been included in the data set.\r\n" + + string.Join(",\r\n", problematicEndPoints))); throw _exceptions.First(); } else From d22742a715eec611bafc5d00c7163cb7752171a6 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Mon, 14 Mar 2022 15:52:57 +0100 Subject: [PATCH 14/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 4c390aba9e..1f87c49ef6 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -88,6 +88,36 @@ public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges () Assert.That(orderTicket.State.IsRelationChanged, Is.False); } + [Test] + public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges_IncludeOnlyForeignKeySide_ThrowsInvalidOperationException () + { + var order1 = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + var order2 = (Order)LifetimeService.NewObject(TestableClientTransaction,typeof(Order), ParamList.Empty); + order1.OrderTicket.EnsureDataAvailable(); + + var orderTicket = order1.OrderTicket; + orderTicket.Order = order2; + + Assert.That(order1.State.IsChanged, Is.True); + Assert.That(order1.State.IsRelationChanged, Is.True); + Assert.That(order2.State.IsNew, Is.True); + Assert.That(order2.State.IsRelationChanged, Is.True); + Assert.That(orderTicket.State.IsChanged, Is.True); + Assert.That(orderTicket.State.IsDataChanged, Is.True); + Assert.That(orderTicket.State.IsRelationChanged, Is.True); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + + Assert.That( + () => UnloadFiltered(obj => obj == orderTicket), + Throws.InvalidOperationException + .With.Message.EqualTo( + "Cannot unload the following relation endpoints because the associated domain objects have not been included in the data set.\r\n" + + orderTicket.ID + "/Remotion.Data.DomainObjects.UnitTests.TestDomain.OrderTicket.Order,\r\n" + + order2.ID + "/Remotion.Data.DomainObjects.UnitTests.TestDomain.Order.OrderTicket")); + } + [Test] public void UnloadFiltered_WithAllObjects_WithDomainObjectCollectionRelation_WithoutChanges () { From bba1c7fa4b38f90d0d72a7b466893f3d65be44b8 Mon Sep 17 00:00:00 2001 From: Michael Ketting Date: Thu, 17 Mar 2022 16:45:49 +0100 Subject: [PATCH 15/15] RM-8240 - temp --- .../UnloadFilteredDomainObjectsCommantTest.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs index 1f87c49ef6..ec1890190b 100644 --- a/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs +++ b/Remotion/Data/DomainObjects.UnitTests/IntegrationTests/Unload/UnloadFilteredDomainObjectsCommantTest.cs @@ -118,6 +118,47 @@ public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges_Inclu + order2.ID + "/Remotion.Data.DomainObjects.UnitTests.TestDomain.Order.OrderTicket")); } + [Test] + public void UnloadFiltered_WithAllObjects_WithOneToOneRelation_WithChanges_IncludeVirtualSide () + { + var order1 = (Order)LifetimeService.GetObject(TestableClientTransaction, DomainObjectIDs.Order1, false); + var order2 = (Order)LifetimeService.NewObject(TestableClientTransaction,typeof(Order), ParamList.Empty); + order1.OrderTicket.EnsureDataAvailable(); + + var orderTicket = order1.OrderTicket; + orderTicket.Order = order2; + + Assert.That(order1.State.IsChanged, Is.True); + Assert.That(order1.State.IsRelationChanged, Is.True); + Assert.That(order2.State.IsNew, Is.True); + Assert.That(order2.State.IsRelationChanged, Is.True); + Assert.That(orderTicket.State.IsChanged, Is.True); + Assert.That(orderTicket.State.IsDataChanged, Is.True); + Assert.That(orderTicket.State.IsRelationChanged, Is.True); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + var orderTicketEndPoints = TestableClientTransaction.DataManager.RelationEndPoints.Where(ep => ep.Definition.ClassDefinition.ClassType == typeof(OrderTicket)).ToArray(); + + UnloadFiltered(obj => obj == order1 || obj == order2); + + Assert.That(TestableClientTransaction.DataManager.DataContainers, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.DataContainers.Select(dc => dc.DomainObject), Has.No.AnyOf(orderTicket)); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints, Is.Not.Empty); + Assert.That(TestableClientTransaction.DataManager.RelationEndPoints.Select(ep => ep.Definition), Has.No.AnyOf(orderTicketEndPoints)); + + Assert.That(order1.State.IsUnchanged, Is.True); + Assert.That(order1.State.IsRelationChanged, Is.False); + Assert.That(order2.State.IsNew, Is.True); + Assert.That(order2.State.IsRelationChanged, Is.False); + Assert.That(orderTicket.State.IsNotLoadedYet, Is.True); + Assert.That(orderTicket.State.IsRelationChanged, Is.False); + + orderTicket.EnsureDataAvailable(); + Assert.That(orderTicket.Order, Is.SameAs(order1)); + Assert.That(order2.OrderTicket, Is.Null); + } + [Test] public void UnloadFiltered_WithAllObjects_WithDomainObjectCollectionRelation_WithoutChanges () {