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