/// <summary>
 /// Invokes the 'CustomOp_Child' method of the specified <see cref="Child"/> entity.
 /// </summary>
 /// <param name="child">The <see cref="Child"/> entity instance.</param>
 public void CustomOp_Child(Child child)
 {
     child.CustomOp_Child();
 }
 public void CustomOp_Child(Child child)
 {
     ((CompositionEntityBase)child).OperationResult += ",CustomOp_Child";
 }
 private void DetachChildren(Child entity)
 {
     entity.Parent = null;
 }
 private bool FilterChildren(Child entity)
 {
     return (entity.ParentID == this.ID);
 }
        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;
        }
 private void AttachChildren(Child entity)
 {
     entity.Parent = this;
 }
        /// <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>
        /// 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 Entity_RemoveParent()
        {
            ConfigurableEntityContainer container = new ConfigurableEntityContainer();
            container.CreateSet<Parent>(EntitySetOperations.All);
            container.CreateSet<Child>(EntitySetOperations.All);
            container.CreateSet<GrandChild>(EntitySetOperations.All);
            EntitySet<Parent> parentSet = container.GetEntitySet<Parent>();
            EntitySet<Child> childSet = container.GetEntitySet<Child>();

            Parent parent1 = new Parent() { ID = 1 };
            Parent parent2 = new Parent() { ID = 2 };
            Child child = new Child() { 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.");

            parentSet.Remove(parent1);
            EntityChangeSet changeSet = container.GetChanges();
            Assert.AreEqual(2, changeSet.Count());
        }
        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();
        }
        public void Composition_InvalidParentUpdate_EntityCollection()
        {
            ConfigurableEntityContainer container = new ConfigurableEntityContainer();
            container.CreateSet<Parent>(EntitySetOperations.All);
            container.CreateSet<Child>(EntitySetOperations.All);
            container.CreateSet<GrandChild>(EntitySetOperations.All);
            EntitySet<Parent> parentSet = container.GetEntitySet<Parent>();
            EntitySet<Child> childSet = container.GetEntitySet<Child>();

            Parent parent1 = new Parent() { ID = 1 };
            Parent parent2 = new Parent() { ID = 2 };
            Child child = new Child() { 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_ParentUpdateOnDelete()
        {
            ConfigurableEntityContainer container = new ConfigurableEntityContainer();
            container.CreateSet<Parent>(EntitySetOperations.All);
            container.CreateSet<Child>(EntitySetOperations.All);
            container.CreateSet<GrandChild>(EntitySetOperations.All);
            EntitySet<Parent> parentSet = container.GetEntitySet<Parent>();
            EntitySet<Child> childSet = container.GetEntitySet<Child>();

            Parent parent1 = new Parent() { ID = 1 };
            Child child1 = new Child() { ID = 1, Parent = parent1 };
            container.LoadEntities(new Entity[] { parent1, child1 });

            // insert new parent and child
            Parent newParent = new Parent();
            Child newChild = new Child();
            newChild.Parent = newParent;
            parentSet.Add(newParent);

            // When the parent is removed, all it's children are also removed.
            // This causes the child ParentID to be set to 0, which means it now
            // matches the new Parent.Children collection. The removed child is added to
            // the new parent's collection, which causes child.Parent to be updated.
            // However that add is transitory - once the child is removed from the
            // entity set, it is removed from the new entity's child collection.
            // That invalid update will result in an exception on submit.
            parentSet.Remove(parent1);
            Assert.AreEqual(1, newParent.Children.Count);
            Assert.IsTrue(newParent.Children.Contains(newChild));

            // if we allow the transitory phase, on accept changes we have to be
            // sure things are fixed up
            Assert.AreEqual(EntityState.Deleted, parent1.EntityState);
            Assert.AreEqual(EntityState.Deleted, child1.EntityState);
            Assert.AreSame(newParent, newChild.Parent);

            // changeset validation succeeds, since the parent is deleted along
            // with the child
            EntityChangeSet changeSet = container.GetChanges();
            Assert.IsTrue(changeSet.AddedEntities.Count == 2 && changeSet.RemovedEntities.Count == 2);
            ChangeSetBuilder.CheckForInvalidUpdates(changeSet);
        }
        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));
        }
        public void InsertChild(Child child)
        {
            SetOperationInvoked(child);

            ((CompositionEntityBase)child).OperationResult = "Insert";
        }
        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 DeleteChild(Child child)
        {
            SetOperationInvoked(child);

            ((CompositionEntityBase)child).OperationResult = "Delete";
        }
 private bool FilterParent(Child entity)
 {
     return (entity.ID == this.ParentID);
 }
 public void CustomOp_Child(Child child)
 {
     this.SetOperationInvoked(child, "CustomOp_Child");
     ((CompositionEntityBase)child).OperationResult += ",CustomOp_Child";
 }
        public void Composition_AddChildPreviouslyReferencingOtherParent()
        {
            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 p1 = new Parent() { ID = 1 };
            Parent p2 = new Parent() { ID = 2 };
            container.LoadEntities(new Entity[] { p1, p2 });

            Child child = new Child() { ID = 1, ParentID = 2 };
            p1.Children.Add(child);

            Assert.AreEqual(EntityState.Modified, p1.EntityState);
            Assert.IsTrue(childSet.HasChanges);
        }