/// <summary>
        /// Move descendant elements with parent node specified as 'from' to new parent node, specified as 'to'.
        /// </summary>
        /// <typeparam name="T">Type of elements. Should implement IHasTreeEntry interface. </typeparam>
        /// <param name="collection">Collection containing hierarchical data. </param>
        /// <param name="from">Parent node from where to move descendants. </param>
        /// <param name="to">Node to move descendants into. </param>
        /// <returns>List of TreeEntry items with recalculated positions. </returns>
        public static IEnumerable <TreeEntry> MoveSubtree <T>(this DbSet <T> collection, T from, T to)
            where T : class, IHasTreeEntry
        {
            var sourceInterval = collection.EnsureTreeEntryLoaded(from);
            var targetInterval = collection.EnsureTreeEntryLoaded(to);

            var relocation =
                NestedIntervalMath.BuildSubtreeRelocationMatrix(sourceInterval, targetInterval, 1);

            var depthDiff = targetInterval.Depth - sourceInterval.Depth;

            var elementsToUpdate = collection.Include(i => i.TreeEntry)
                                   .DescendantsOf(from)
                                   .ToList();

            var intervalsToUpdate = elementsToUpdate
                                    .Select(i => i.TreeEntry)
                                    .ToList();

            foreach (var item in elementsToUpdate)
            {
                item.TreeEntry.SetFromInterval(NestedIntervalMath.MultiplyMatrixToInterval(relocation, item.TreeEntry));
                item.TreeEntry.Depth += depthDiff;
            }

            collection.UpdateRange(elementsToUpdate);

            return(intervalsToUpdate);
        }
Example #2
0
        public void RelocatedTreeHasSameValuesAsCreated()
        {
            var firstRoot       = NestedIntervalMath.GetIntervalByPosition(1);
            var firstChild      = NestedIntervalMath.GetIntervalByPosition(firstRoot, 2);
            var firstChildChild = NestedIntervalMath.GetIntervalByPosition(firstChild, 1);

            var secondRoot       = NestedIntervalMath.GetIntervalByPosition(2);
            var secondChild      = NestedIntervalMath.GetIntervalByPosition(secondRoot, 5);       // < - should be same as relocated firstroot
            var secondchildchild = NestedIntervalMath.GetIntervalByPosition(secondChild, 2);      // < - should be same as relocated firstchild
            var second3child     = NestedIntervalMath.GetIntervalByPosition(secondchildchild, 1); // < - should be same as relocated firstchildchild

            var relocationMatrix   = NestedIntervalMath.BuildSubtreeRelocationMatrix(firstRoot, secondRoot, 5);
            var firstRootRelocated =
                NestedIntervalMath.MultiplyMatrixToInterval(relocationMatrix, firstRoot);
            var firstChildRelocated = NestedIntervalMath.MultiplyMatrixToInterval(relocationMatrix, firstChild);

            var firstChildChildRelocated =
                NestedIntervalMath.MultiplyMatrixToInterval(relocationMatrix, firstChildChild);

            firstRootRelocated.Depth       = 2; // Just set depth for equality test. it's not computed in math multiplication
            firstChildRelocated.Depth      = 3;
            firstChildChildRelocated.Depth = 4;
            Assert.AreEqual(secondChild, firstRootRelocated);
            Assert.AreEqual(secondchildchild, firstChildRelocated);
            Assert.AreEqual(second3child, firstChildChildRelocated);
        }
Example #3
0
        /// <summary>
        /// Build predicate selecting elements by it's positions relative to parent.
        /// </summary>
        /// <example>
        /// For path {2, 1, 3} it will select first root item, then first child of 2nd root item, then third child of 2nd root's first child.
        /// </example>
        /// <param name="positionsPath">Elements positions path relative to it's parent. Position indexes starting from 1.</param>
        /// <returns>Predicate selecting list of elements by given path. </returns>
        public static TreeFilter ElementsByPositionsPath(IEnumerable <long> positionsPath)
        {
            TreeFilter         conditions     = PredicateBuilder.False <TreeEntry>();
            IIntervalQuadruple parentInterval = null;

            foreach (var position in positionsPath)
            {
                var childInterval = NestedIntervalMath.GetIntervalByPosition(parentInterval, position);
                conditions.Or(i => i.Nv == childInterval.Nv && i.Dv == childInterval.Dv);
                parentInterval = childInterval;
            }

            return(conditions);
        }
        /// <summary>
        /// Add item to hierarchy as next child of given parent item.
        /// </summary>
        /// <param name="collection">Entity Framework Collection to add item. </param>
        /// <param name="parent">Item's parent in hierarchy. </param>
        /// <param name="item">Element to add to hierarchy. </param>
        /// <typeparam name="T">Type of elements in collection. Should Implement IHasTreeEntry. </typeparam>
        /// <returns>Entity Framework EntityEntry object. </returns>
        public static EntityEntry <T> AddNextChild <T>(this DbSet <T> collection, T parent, T item)
            where T : class, IHasTreeEntry
        {
            TreeEntry parentEntry = null;

            if (parent != null)
            {
                parentEntry = collection.EnsureTreeEntryLoaded(parent);
            }

            var lastInterval = GetLastInsertedChildInterval(collection, true, parent);

            var nextPosition = NestedIntervalMath.GetPositionByInterval(parentEntry, lastInterval) + 1;

            var treeEntry = new TreeEntry(NestedIntervalMath.GetIntervalByPosition(parentEntry, nextPosition));

            item.TreeEntry = treeEntry;

            var state = collection.Add(item);

            // state.Reference(i => i.TreeEntry).EntityEntry.State = EntityState.Detached;
            return(state);
        }