/// <summary> /// Create a 4 level compositional hierarchy that includes both /// collection and singleton compositions /// </summary> public static List <Parent> CreateCompositionHierarchy() { int parentKey = 1; int childKey = 1; int grandChildKey = 1; int greatGrandChildKey = 1; List <Parent> parents = new List <Parent>(); for (int i = 0; i < 3; i++) { Parent p = new Parent { ID = parentKey++ }; parents.Add(p); for (int j = 0; j < 3; j++) { Child c = new Child { ID = childKey++, ParentID = p.ID, Parent = p }; p.Children.Add(c); for (int k = 0; k < 3; k++) { GrandChild gc = new GrandChild { ID = grandChildKey++, ParentID = c.ID, Parent = c }; c.Children.Add(gc); // add singleton child to grand child gc.Child = new GreatGrandChild { ID = greatGrandChildKey++, ParentID = gc.ID, Parent = gc }; } } } return(parents); }
public void CustomOp_GrandChild(GrandChild grandChild) { ((CompositionEntityBase)grandChild).OperationResult += ",CustomOp_GrandChild"; }
public void CustomOp_GrandChild(GrandChild grandChild) { this.SetOperationInvoked(grandChild, "CustomOp_GrandChild"); ((CompositionEntityBase)grandChild).OperationResult += ",CustomOp_GrandChild"; }
/// <summary> /// Invokes the 'CustomOp_GrandChild' method of the specified <see cref="GrandChild"/> entity. /// </summary> /// <param name="grandChild">The <see cref="GrandChild"/> entity instance.</param> public void CustomOp_GrandChild(GrandChild grandChild) { grandChild.CustomOp_GrandChild(); }
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); }
public void InsertGrandChild(GrandChild grandChild) { SetOperationInvoked(grandChild); ((CompositionEntityBase)grandChild).OperationResult = "Insert"; }
public void UpdateGrandChild(GrandChild grandChild) { SetOperationInvoked(grandChild); ((CompositionEntityBase)grandChild).OperationResult = "Update"; }
[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> /// 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_InvalidParentUpdate_EntityRef() { ConfigurableEntityContainer container = new ConfigurableEntityContainer(); container.CreateSet<GrandChild>(EntitySetOperations.All); container.CreateSet<GreatGrandChild>(EntitySetOperations.All); EntitySet<GrandChild> parentSet = container.GetEntitySet<GrandChild>(); EntitySet<GreatGrandChild> childSet = container.GetEntitySet<GreatGrandChild>(); GrandChild parent1 = new GrandChild() { ID = 1 }; GrandChild parent2 = new GrandChild() { ID = 2 }; GreatGrandChild child = new GreatGrandChild() { ID = 1, Parent = parent1 }; parentSet.Attach(parent1); parentSet.Attach(parent2); Assert.IsTrue(childSet.IsAttached(child), "Child was not attached automatically."); Assert.AreSame(parent1, ((Entity)child).Parent, "Entity.Parent doesn't reflect the parent-child relationship."); // point the child to a new parent, which results // in the child being reparented (an invalid update) child.Parent = parent2; EntityChangeSet changeSet = container.GetChanges(); Assert.AreEqual(EntityState.Modified, child.EntityState); Assert.AreEqual(EntityState.Modified, parent1.EntityState); Assert.AreEqual(EntityState.Modified, parent2.EntityState); // Verify that changing the parent throws an exception when // changes are validated ExceptionHelper.ExpectInvalidOperationException(delegate { ChangeSetBuilder.CheckForInvalidUpdates(changeSet); }, string.Format(Resource.Entity_CantReparentComposedChild, child)); }
public void Composition_MultipleRemoveReadds() { ConfigurableEntityContainer container = new ConfigurableEntityContainer(); container.CreateSet<Parent>(EntitySetOperations.All); container.CreateSet<Child>(EntitySetOperations.All); container.CreateSet<GrandChild>(EntitySetOperations.All); container.CreateSet<GreatGrandChild>(EntitySetOperations.All); EntitySet<Parent> parentSet = container.GetEntitySet<Parent>(); EntitySet<Child> childSet = container.GetEntitySet<Child>(); EntitySet<GreatGrandChild> ggChildSet = container.GetEntitySet<GreatGrandChild>(); Parent parent = new Parent() { ID = 1 }; Child child = new Child() { ID = 1, Parent = parent }; container.LoadEntities(new Entity[] { parent, child }); parent.Children.Remove(child); Assert.AreEqual(EntityState.Deleted, child.EntityState); Assert.IsFalse(childSet.Contains(child)); parent.Children.Add(child); Assert.AreEqual(EntityState.Modified, child.EntityState); Assert.IsTrue(childSet.Contains(child)); parent.Children.Remove(child); Assert.AreEqual(EntityState.Deleted, child.EntityState); Assert.IsFalse(childSet.Contains(child)); // verify the same scenario for a singleton composition GrandChild gc = new GrandChild { ID = 1 }; GreatGrandChild ggc = new GreatGrandChild { ID = 1, ParentID = 1 }; container.LoadEntities(new Entity[] { gc, ggc }); Assert.AreSame(ggc, gc.Child); gc.Child = null; Assert.AreEqual(EntityState.Deleted, ggc.EntityState); Assert.IsFalse(ggChildSet.Contains(ggc)); gc.Child = ggc; Assert.AreEqual(EntityState.Unmodified, ggc.EntityState); Assert.IsTrue(ggChildSet.Contains(ggc)); gc.Child = null; Assert.AreEqual(EntityState.Deleted, ggc.EntityState); Assert.IsFalse(ggChildSet.Contains(ggc)); }
private static Parent CreateCompositionHierarchy(int numChildren, int numGrandChildren) { int parentKey = 1; int childKey = 1; int grandChildKey = 1; int greatGrandChildKey = 1; Parent p = new Parent { ID = parentKey++ }; for (int j = 0; j < numChildren; j++) { Child c = new Child { ID = childKey++, ParentID = p.ID, Parent = p }; p.Children.Add(c); for (int k = 0; k < numGrandChildren; k++) { GrandChild gc = new GrandChild { ID = grandChildKey++, ParentID = c.ID, Parent = c }; c.Children.Add(gc); // add singleton child to grand child gc.Child = new GreatGrandChild { ID = greatGrandChildKey++, ParentID = gc.ID, Parent = gc }; } } VerifyHierarchy(p); return p; }
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> /// Create a 4 level compositional hierarchy that includes both /// collection and singleton compositions /// </summary> public static List<Parent> CreateCompositionHierarchy() { int parentKey = 1; int childKey = 1; int grandChildKey = 1; int greatGrandChildKey = 1; List<Parent> parents = new List<Parent>(); for (int i = 0; i < 3; i++) { Parent p = new Parent { ID = parentKey++ }; parents.Add(p); for (int j = 0; j < 3; j++) { Child c = new Child { ID = childKey++, ParentID = p.ID, Parent = p }; p.Children.Add(c); for (int k = 0; k < 3; k++) { GrandChild gc = new GrandChild { ID = grandChildKey++, ParentID = c.ID, Parent = c }; c.Children.Add(gc); // add singleton child to grand child gc.Child = new GreatGrandChild { ID = greatGrandChildKey++, ParentID = gc.ID, Parent = gc }; } } } return parents; }
[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); }
private void AttachChildren(GrandChild entity) { entity.Parent = this; }
private void DetachChildren(GrandChild entity) { entity.Parent = null; }
private bool FilterChildren(GrandChild entity) { return (entity.ParentID == this.ID); }
public void Changeset_GetAssociatedChanges_Singleton() { DomainServiceDescription dsd = DomainServiceDescription.GetDescription(typeof(CompositionScenarios_Explicit)); // verify singleton change of None GreatGrandChild unmodifiedGgc = new GreatGrandChild(); GrandChild currGrandChild = new GrandChild { Child = unmodifiedGgc }; GrandChild origGrandChild = new GrandChild { Child = unmodifiedGgc }; ChangeSetEntry gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update); gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } }; ChangeSetEntry ggcOperation = new ChangeSetEntry(2, unmodifiedGgc, null, DomainOperation.None); ChangeSet cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation }); GreatGrandChild ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.None).Cast<GreatGrandChild>().SingleOrDefault(); Assert.AreSame(unmodifiedGgc, ggcChange); // verify singleton insert GreatGrandChild newGgc = new GreatGrandChild(); currGrandChild = new GrandChild { Child = newGgc }; origGrandChild = new GrandChild { Child = null }; gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update); gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } }; ggcOperation = new ChangeSetEntry(2, newGgc, null, DomainOperation.Insert); cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation }); ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Insert).Cast<GreatGrandChild>().SingleOrDefault(); Assert.AreSame(newGgc, ggcChange); Assert.AreEqual(ChangeOperation.Insert, cs.GetChangeOperation(newGgc)); // verify singleton update GreatGrandChild modifiedGgc = new GreatGrandChild(); currGrandChild = new GrandChild { Child = modifiedGgc }; origGrandChild = new GrandChild { Child = modifiedGgc }; gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update); gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } }; ggcOperation = new ChangeSetEntry(2, modifiedGgc, unmodifiedGgc, DomainOperation.Update); cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation }); ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Update).Cast<GreatGrandChild>().SingleOrDefault(); Assert.AreSame(modifiedGgc, ggcChange); Assert.AreSame(unmodifiedGgc, cs.GetOriginal(modifiedGgc)); Assert.AreEqual(ChangeOperation.Update, cs.GetChangeOperation(modifiedGgc)); // verify singleton delete GreatGrandChild deletedGgc = new GreatGrandChild(); currGrandChild = new GrandChild { Child = null }; origGrandChild = new GrandChild { Child = deletedGgc }; gcOperation = new ChangeSetEntry(1, currGrandChild, null, DomainOperation.Update); gcOperation.OriginalAssociations = new Dictionary<string, int[]>() { { "Child", new int[] { 2 } } }; ggcOperation = new ChangeSetEntry(2, deletedGgc, null, DomainOperation.Delete); gcOperation.OriginalAssociations = new Dictionary<string, int[]>() { { "Child", new int[] { 2 } } }; cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation }); ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Delete).Cast<GreatGrandChild>().SingleOrDefault(); Assert.AreSame(deletedGgc, ggcChange); Assert.AreSame(null, cs.GetOriginal(deletedGgc)); Assert.AreEqual(ChangeOperation.Delete, cs.GetChangeOperation(deletedGgc)); }