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