public void Composition_ModifiedParentWithModifiedChildren() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); LoadOperation lo = ctxt.Load(ctxt.GetParentsQuery(), false); SubmitOperation so = null; EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); Parent parent = ctxt.Parents.First(); // remove the child and modify the parent Child child = parent.Children.First(); parent.Children.Remove(child); parent.Property += "*"; so = ctxt.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); }); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { Assert.IsFalse(so.HasError); Assert.IsFalse(ctxt.HasChanges); }); EnqueueTestComplete(); }
public void HierarchicalChangeTracking_AcceptChanges_Bug793490() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // remove the parent and verify that the delete cascades ctxt.Parents.Remove(parent); int totalCount = 1 + 3 + 9 + 9; EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.AreEqual(totalCount, cs.RemovedEntities.Count); Assert.AreEqual(EntityState.Deleted, parent.EntityState); List<Entity> removedEntities = new List<Entity>(cs.RemovedEntities.Cast<Entity>()); removedEntities.Remove(parent); removedEntities.Insert(0, parent); foreach (Entity entity in removedEntities) { ((IRevertibleChangeTracking)entity).AcceptChanges(); } // verify that removes were accepted Assert.IsTrue(removedEntities.All(p => p.EntityState == EntityState.Detached)); Assert.AreEqual(0, ctxt.Parents.Count); Assert.AreEqual(0, ctxt.EntityContainer.GetEntitySet<Child>().Count); Assert.AreEqual(0, ctxt.EntityContainer.GetEntitySet<GrandChild>().Count); Assert.AreEqual(0, ctxt.EntityContainer.GetEntitySet<GreatGrandChild>().Count); // verify all changes were undone and the graph // references are restored cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); }
private void MultipleChildCustomMethodInvocations(Uri testUri) { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(testUri); Parent parent = null; Child child = null; GrandChild grandChild = null; GreatGrandChild greatGrandChild = null; SubmitOperation so = null; IEnumerable<Entity> expectedUpdates = null; LoadOperation lo = ctxt.Load(ctxt.GetParentsQuery(), false); EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); parent = ctxt.Parents.First(); child = parent.Children.Skip(2).Take(1).Single(); grandChild = child.Children.Skip(1).Take(1).Single(); greatGrandChild = parent.Children.First().Children.Skip(1).Take(1).Single().Child; // invoke on the leaf node greatGrandChild.CustomOp_GreatGrandChild(); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 4 && cs.RemovedEntities.Count == 0); // invoke on a child child.CustomOp_Child(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 5 && cs.RemovedEntities.Count == 0); // invoke on a grand child grandChild.CustomOp_GrandChild(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 6 && cs.RemovedEntities.Count == 0); // invoke on the parent parent.CustomOp_Parent(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 6 && cs.RemovedEntities.Count == 0); expectedUpdates = cs.Where(p => p.HasChildChanges || p.HasPropertyChanges).ToArray(); so = ctxt.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); }); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { this.VerifySuccess(ctxt, so, expectedUpdates); Assert.IsTrue(parent.OperationResult.Split(',').Contains("CustomOp_Parent")); Assert.IsTrue(child.OperationResult.Split(',').Contains("CustomOp_Child")); Assert.IsTrue(grandChild.OperationResult.Split(',').Contains("CustomOp_GrandChild")); Assert.IsTrue(greatGrandChild.OperationResult.Split(',').Contains("CustomOp_GreatGrandChild")); }); EnqueueTestComplete(); }
/// <summary> /// Delete an entire hierarchy by removing the parent and verifying /// that all children are recursively removed automatically. /// </summary> private void HierarchyDelete(Uri testUri) { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(testUri); Parent parent = null; SubmitOperation so = null; LoadOperation lo = ctxt.Load(ctxt.GetParentsQuery(), false); EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); parent = ctxt.Parents.First(); ctxt.Parents.Remove(parent); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.AreEqual(1 + 3 + 9 + 9, cs.RemovedEntities.Count); so = ctxt.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); }); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { this.VerifySuccess(ctxt, so, Enumerable.Empty<Entity>()); }); EnqueueTestComplete(); }
public void HierarchicalChangeTracking_ChildAssociationUpdates_EntityCollection() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // Remove a child and add one Child c = parent.Children.First(); GrandChild gc = c.Children.ToArray()[1]; GreatGrandChild ggc = gc.Child; gc.Child = null; // remove GrandChild newGc = new GrandChild { ID = 100 }; GreatGrandChild newGgc = new GreatGrandChild { ID = 100 }; c.Children.Add(newGc); // add newGc.Child = newGgc; Assert.IsTrue(parent.HasChanges); Assert.IsTrue(c.HasChanges); Assert.IsTrue(gc.HasChanges); Assert.IsFalse(ggc.HasChanges); Assert.AreEqual(EntityState.Deleted, ggc.EntityState); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 2 && cs.ModifiedEntities.Count == 3 && cs.RemovedEntities.Count == 1); // Reject the remove and verify state ((IRevertibleChangeTracking)gc).RejectChanges(); Assert.IsTrue(parent.HasChanges); Assert.IsTrue(c.HasChanges); Assert.IsFalse(gc.HasChanges); Assert.IsFalse(ggc.HasChanges); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 2 && cs.ModifiedEntities.Count == 2 && cs.RemovedEntities.Count == 0); Assert.AreSame(ggc, gc.Child); ((IRevertibleChangeTracking)c).RejectChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 0 && cs.RemovedEntities.Count == 0); }
/// <summary> /// Make a bunch of multilevel updates to a hierarchy /// </summary> private void HierarchyUpdate(Uri testUri) { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(testUri); Parent parent = null; SubmitOperation so = null; LoadOperation lo = ctxt.Load(ctxt.GetParentsQuery(), false); IEnumerable<Entity> expectedUpdates = null; EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); parent = ctxt.Parents.First(); Child existingChild = parent.Children.First(); Assert.AreSame(parent, ((Entity)existingChild).Parent); GrandChild existingGrandChild = existingChild.Children.Skip(1).Take(1).Single(); // add a few new children Child newChild = new Child { ID = 100 }; parent.Children.Add(newChild); GrandChild newGc = new GrandChild { ID = 100 }; existingChild.Children.Add(newGc); GreatGrandChild newGgc = new GreatGrandChild { ID = 100 }; // update a few children GrandChild updatedGrandChild = existingChild.Children.First(p => p.EntityState != EntityState.New); updatedGrandChild.Property += "x"; // remove a few children GreatGrandChild deletedGreatGrandChild = existingGrandChild.Child; existingGrandChild.Child = null; // invoke a custom method on the parent parent.CustomOp_Parent(); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 2 && cs.ModifiedEntities.Count == 4 && cs.RemovedEntities.Count == 1); Assert.IsTrue(cs.AddedEntities.Contains(newChild)); Assert.IsTrue(cs.AddedEntities.Contains(newGc)); Assert.IsTrue(cs.ModifiedEntities.Contains(parent)); Assert.IsTrue(cs.ModifiedEntities.Contains(existingChild)); Assert.IsTrue(cs.ModifiedEntities.Contains(existingGrandChild)); Assert.IsTrue(cs.ModifiedEntities.Contains(updatedGrandChild)); Assert.IsTrue(cs.RemovedEntities.Contains(deletedGreatGrandChild)); // direct test verifying that we create the correct set of // ChangeSetEntries to send to the server int modifiedCount = cs.AddedEntities.Count + cs.ModifiedEntities.Count + cs.RemovedEntities.Count; int expectedOperationCount = 24; IEnumerable<ChangeSetEntry> entityOps = ChangeSetBuilder.Build(cs); int entityOpCount = entityOps.Count(); Assert.AreEqual(expectedOperationCount, entityOpCount); Assert.AreEqual(expectedOperationCount - modifiedCount, entityOps.Count(p => p.Operation == EntityOperationType.None)); // verify that original associations are set up correctly this.ValidateEntityOperationAssociations(entityOps); expectedUpdates = cs.Where(p => p.HasChildChanges || p.HasPropertyChanges).ToArray(); so = ctxt.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); }); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { this.VerifySuccess(ctxt, so, expectedUpdates); // verify that the custom method was invoked string[] updateResults = parent.OperationResult.Split(','); Assert.IsTrue(updateResults.Contains("CustomOp_Parent")); }); EnqueueTestComplete(); }
public void Composition_DeleteAndAddChildWithSameKey() { CompositionScenarios_Explicit ctx = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); LoadOperation lo = ctx.Load(ctx.GetParentsQuery(), false); SubmitOperation so = null; int childID = 0; int parentID = 0; EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); // Find any parent and delete a child. Parent parent = ctx.Parents.First(); Child child = parent.Children.First(); parent.Children.Remove(child); // Add a new child with the same key as the one deleted. Child newChild = new Child { ID = child.ID, ParentID = child.ParentID, Parent = child.Parent, Property = "NewProp" }; parent.Children.Add(newChild); // Save the parent and child keys so that we can check later if the child got added properly. childID = child.ID; parentID = parent.ID; so = ctx.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); }); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { Assert.IsFalse(so.HasError); Assert.IsFalse(ctx.HasChanges); // Reload the entities. ctx.EntityContainer.Clear(); lo = ctx.Load(ctx.GetParentsQuery(), false); }); EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); // Make sure the new child got added properly. Parent parent1 = ctx.Parents.Where(p => p.ID == parentID).SingleOrDefault(); Assert.IsNotNull(parent1); Assert.IsNotNull(parent1.Children.Where(c => c.ID == childID && c.Property == "NewProp")); }); EnqueueTestComplete(); }
[WorkItem(188918)] // CSDMain public void HierarchicalChangeTracking_AcceptChanges_MultipleChildChanges() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // update the child first Child child = parent.Children.First(); child.OperationResult = "complete"; // update the grandchild child.Children.First().OperationResult = "complete"; // add a new grandchild GrandChild newGrandChild = new GrandChild { ID = 25 }; child.Children.Add(newGrandChild); // the key to this bug is accepting the child changes BEFORE // the grand child changes. ((IChangeTracking)child).AcceptChanges(); ((IChangeTracking)newGrandChild).AcceptChanges(); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); Assert.AreEqual(EntityState.Unmodified, child.EntityState); Assert.AreEqual(EntityState.Unmodified, newGrandChild.EntityState); }
public void HierarchicalChangeTracking_RejectChanges() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // make some changes Child child = parent.Children.ToArray()[1]; GrandChild grandChild = child.Children.First(); GreatGrandChild greatGrandChild = grandChild.Child; greatGrandChild.OperationResult += "x"; // edit GrandChild addedGrandChild = new GrandChild(); child.Children.Add(addedGrandChild); // add GreatGrandChild removedGreatGrandChild = child.Children.ToArray()[1].Child; child.Children.ToArray()[1].Child = null; // remove EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 5 && cs.RemovedEntities.Count == 1); // call reject at various levels of the hierarchy and verify // undo the delete of the GreatGrandChild's Child (an edit and a remove undone) ((IRevertibleChangeTracking)child.Children.ToArray()[1]).RejectChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 4 && cs.RemovedEntities.Count == 0); // ensure reference was reestablished Assert.AreSame(child.Children.ToArray()[1], removedGreatGrandChild.Parent); // undo the edit to the GreatGrandChild (2 edits undone) ((IRevertibleChangeTracking)greatGrandChild).RejectChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 2 && cs.RemovedEntities.Count == 0); // undo the add to the Child's Children collection (1 add and remaining 2 edits undone) ((IRevertibleChangeTracking)child).RejectChanges(); Assert.IsFalse(child.Children.Contains(addedGrandChild)); Assert.AreEqual(EntityState.Detached, addedGrandChild.EntityState); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); VerifyHierarchy(parent); }
/// <summary> /// Verify successful completion of an update operation /// </summary> private void VerifySuccess(CompositionScenarios_Explicit ctxt, SubmitOperation so, IEnumerable<Entity> expectedUpdates) { // verify operation completed successfully TestHelperMethods.AssertOperationSuccess(so); // verify that all operations were executed EntityChangeSet cs = so.ChangeSet; VerifyOperationResults(cs.AddedEntities, expectedUpdates); // verify that all changes have been accepted cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); }
public void HierarchicalChangeTracking_AcceptChanges() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // make some changes Child child = parent.Children.ToArray()[1]; GrandChild grandChild = child.Children.ToArray()[0]; GreatGrandChild greatGrandChild = grandChild.Child; greatGrandChild.OperationResult += "x"; // edit child.Children.Add(new GrandChild() { ID = 123 }); // add child.Children.ToArray()[1].Child = null; // remove EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 5 && cs.RemovedEntities.Count == 1); // call accept at various levels of the hierarchy and verify // accept the delete of the GreatGrandChild's Child (an edit and a remove undone) ((IRevertibleChangeTracking)child.Children.ToArray()[1]).AcceptChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 4 && cs.RemovedEntities.Count == 0); // accept the edit to the GreatGrandChild (2 edits undone) ((IRevertibleChangeTracking)greatGrandChild).AcceptChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 2 && cs.RemovedEntities.Count == 0); // accept the add to the Child's Children collection (1 add and remaining 2 edits undone) ((IRevertibleChangeTracking)child).AcceptChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); VerifyHierarchy(parent); }
public void HierarchicalChangeTracking_RemoveHierarchy() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // remove the parent and verify that the delete cascades ctxt.Parents.Remove(parent); int totalCount = 1 + 3 + 9 + 9; EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.AreEqual(totalCount, cs.RemovedEntities.Count); Assert.AreEqual(EntityState.Deleted, parent.EntityState); // To reject all child changes by calling reject on the parent, // then readd the parent to undo the delete. ((IRevertibleChangeTracking)parent).RejectChanges(); ctxt.Parents.Add(parent); // verify all changes were undone and the graph // references are restored cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); VerifyHierarchy(parent); }
public void HierarchicalChangeTracking_DetachHierarchy() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); var parentSet = ctxt.EntityContainer.GetEntitySet<Parent>(); var childSet = ctxt.EntityContainer.GetEntitySet<Child>(); var grandChildSet = ctxt.EntityContainer.GetEntitySet<GrandChild>(); var greatGrandChildSet = ctxt.EntityContainer.GetEntitySet<GreatGrandChild>(); // verify all entities are in the attached / Unmodified state Entity[] allEntities = parentSet.Cast<Entity>().Concat(childSet.Cast<Entity>().Concat(grandChildSet.Cast<Entity>().Concat(greatGrandChildSet.Cast<Entity>()))).ToArray(); Assert.IsTrue(allEntities.All(p => p.EntityState == EntityState.Unmodified)); Assert.AreEqual(1, parentSet.Count); Assert.AreEqual(3, childSet.Count); Assert.AreEqual(9, grandChildSet.Count); Assert.AreEqual(9, greatGrandChildSet.Count); // remove the parent and verify that the detach cascades ctxt.Parents.Detach(parent); Assert.AreEqual(0, parentSet.Count); Assert.AreEqual(0, childSet.Count); Assert.AreEqual(0, grandChildSet.Count); Assert.AreEqual(0, greatGrandChildSet.Count); // verify all entities are in the detached state Assert.IsTrue(allEntities.All(p => p.EntityState == EntityState.Detached)); // even though the hierarchy is detached, child entities should remain in their child collections // in the detached state VerifyHierarchy(parent); Assert.AreEqual(3, parent.Children.Count); }
public void HierarchicalChangeTracking_RejectChanges_Bug799365() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // add a new child then reject changes Child newChild = new Child { ID = 25 }; parent.Children.Add(newChild); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 1); ctxt.RejectChanges(); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); Assert.AreEqual(EntityState.Detached, newChild.EntityState); }
public void HierarchyQuery() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); LoadOperation lo = ctxt.Load(ctxt.GetParentsQuery(), false); EnqueueConditional(() => lo.IsComplete); EnqueueCallback(delegate { TestHelperMethods.AssertOperationSuccess(lo); Assert.AreEqual(66, lo.AllEntities.Count()); Assert.AreEqual(3, ctxt.Parents.Count); foreach(Parent p in ctxt.Parents) { VerifyHierarchy(p); } }); EnqueueTestComplete(); }
[WorkItem(188918)] // CSDMain public void HierarchicalChangeTracking_RejectChanges_MultipleChildChanges() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // update the child first Child child = parent.Children.First(); child.OperationResult = "complete"; // update the grandchild child.Children.First().OperationResult = "complete"; // add a new grandchild GrandChild newGrandChild = new GrandChild { ID = 25 }; child.Children.Add(newGrandChild); // Reject changes should revert child before either grandchild (alpha order). We need // to make sure this works with multiple grandchildren. ctxt.RejectChanges(); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty, "cs.IsEmpty is false"); Assert.AreEqual(EntityState.Unmodified, child.EntityState, "EntityState.Unmodified != child.EntityState"); }
/// <summary> /// Test insert of a hierarchy /// </summary> private void HierarchyInsert(Uri testUri) { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(testUri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Add(parent); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.AreEqual(1 + 3 + 9 + 9, cs.AddedEntities.Count); SubmitOperation so = ctxt.SubmitChanges(TestHelperMethods.DefaultOperationAction, null); EnqueueConditional(() => so.IsComplete); EnqueueCallback(delegate { this.VerifySuccess(ctxt, so, Enumerable.Empty<Entity>()); }); EnqueueTestComplete(); }
public void HierarchicalChangeTracking_ChildEdits() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // verify counts Assert.AreEqual(1, ctxt.Parents.Count); EntitySet<Child> childSet = ctxt.EntityContainer.GetEntitySet<Child>(); Assert.AreEqual(3, childSet.Count); EntitySet<GrandChild> grandChildSet = ctxt.EntityContainer.GetEntitySet<GrandChild>(); Assert.AreEqual(9, grandChildSet.Count); EntitySet<GreatGrandChild> greatGrandChildSet = ctxt.EntityContainer.GetEntitySet<GreatGrandChild>(); Assert.AreEqual(9, greatGrandChildSet.Count); // change a child - expect all entities up the hierarchy // to transition to modified Child[] children = parent.Children.ToArray(); GreatGrandChild ggc = children[1].Children.ToArray()[2].Child; ggc.Property += "x"; Assert.IsTrue(ggc.HasChanges); Assert.IsTrue(parent.HasChanges); Assert.IsTrue(children[1].HasChanges); Assert.IsTrue(children[1].Children.ToArray()[2].HasChanges); Assert.IsFalse(children[0].HasChanges); Assert.IsTrue(children[1].Children.ToArray()[2].HasChanges); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 4 && cs.RemovedEntities.Count == 0); // now revert the child change - we expect all changes // to be undone up the hierarchy ((IRevertibleChangeTracking)ggc).RejectChanges(); Assert.IsFalse(ggc.HasChanges); Assert.IsFalse(parent.HasChanges); children = parent.Children.ToArray(); Assert.IsFalse(children[1].HasChanges); Assert.IsFalse(children[0].HasChanges); Assert.IsFalse(children[1].Children.ToArray()[2].HasChanges); Assert.IsFalse(ctxt.EntityContainer.HasChanges); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.IsEmpty); // run the scenario again, this time with some preexisting // updates in the hierarchy - those shouldn't be undone Child c = children[0]; GrandChild gc = c.Children.ToArray()[2]; ggc = gc.Child; gc.Property += "x"; // preexisting update Assert.IsTrue(gc.HasChanges && c.HasChanges && parent.HasChanges); ggc.Property += "x"; Assert.IsTrue(ggc.HasChanges && gc.HasChanges && c.HasChanges && parent.HasChanges); ((IRevertibleChangeTracking)ggc).RejectChanges(); Assert.IsTrue(gc.HasChanges && c.HasChanges && parent.HasChanges); ((IRevertibleChangeTracking)gc).RejectChanges(); Assert.IsTrue(!gc.HasChanges && !c.HasChanges && !parent.HasChanges); }
public void HierarchicalChangeTracking_ChildAssociationUpdates_EntityRef() { CompositionScenarios_Explicit ctxt = new CompositionScenarios_Explicit(CompositionScenarios_Explicit_Uri); Parent parent = CreateCompositionHierarchy(3, 3); ctxt.Parents.Attach(parent); // Remove an EntityRef child - expect all entities up the hierarchy // to transition to modified Child c = parent.Children.First(); GrandChild gc = c.Children.ToArray()[1]; GreatGrandChild ggc = gc.Child; gc.Child = null; Assert.IsTrue(parent.HasChanges); Assert.IsTrue(c.HasChanges); Assert.IsTrue(gc.HasChanges); Assert.IsFalse(ggc.HasChanges); Assert.AreEqual(EntityState.Deleted, ggc.EntityState); EntityChangeSet cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 3 && cs.RemovedEntities.Count == 1); // Reject the remove and verify state ((IRevertibleChangeTracking)gc).RejectChanges(); Assert.IsFalse(parent.HasChanges); Assert.IsFalse(c.HasChanges); Assert.IsFalse(gc.HasChanges); Assert.IsFalse(ggc.HasChanges); cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 0 && cs.ModifiedEntities.Count == 0 && cs.RemovedEntities.Count == 0); Assert.AreSame(ggc, gc.Child); // Now test an EntityRef Add gc.Child = null; ((IChangeTracking)ctxt.EntityContainer).AcceptChanges(); Assert.IsTrue(ctxt.EntityContainer.GetChanges().IsEmpty); gc.Child = new GreatGrandChild { ID = 100 }; cs = ctxt.EntityContainer.GetChanges(); Assert.IsTrue(cs.AddedEntities.Count == 1 && cs.ModifiedEntities.Count == 3 && cs.RemovedEntities.Count == 0); ((IChangeTracking)ctxt.EntityContainer).AcceptChanges(); Assert.IsTrue(ctxt.EntityContainer.GetChanges().IsEmpty); }