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);
        }