예제 #1
0
        public async Task <ContentTree> CloneContentTree(string treeId, string newVersionCode)
        {
            // Use No Tracking since we'll be inserting a new content tree, not updating the source
            var sourceTree = await _connectDb.ContentTrees
                             .AsNoTracking()
                             .Include(x => x.ContentNodes)
                             .FirstOrDefaultAsync(x => x.Id == treeId);

            var sourceNodes = sourceTree.ContentNodes.ToList();
            var nodeKeyMap  = new Dictionary <string, string>();

            if (sourceTree == null)
            {
                throw new NullReferenceException($"Could not locate a record for version id: {treeId}");
            }

            // Initialize the cloned tree
            var clonedTree = new ContentTree
            {
                Id           = Guid.NewGuid().ToString("N"),
                ContentType  = sourceTree.ContentType,
                ContentId    = sourceTree.ContentId,
                VersionCode  = newVersionCode,
                ContentNodes = new List <ContentNode>()
            };

            // Clone the content node structure
            foreach (var node in sourceNodes)
            {
                // Generate a new node key and track old id
                var newNodeId = Guid.NewGuid().ToString("N");

                nodeKeyMap.Add(node.Id, newNodeId);
                node.Id = newNodeId;

                // Instruct the underlying provider to clone a new content instance
                var clonedWidget = _widgetProvider.CloneSettings(node.WidgetType, node.WidgetId);

                // Some content types have static or null models, thus return null clones.
                // This is expected behavior and should result in a null mapping value
                node.WidgetId = clonedWidget?.Id;
            }

            // 2nd pass (efficient) to update parent Ids using the node map
            foreach (var node in sourceNodes)
            {
                if (node.ParentId != null && nodeKeyMap.ContainsKey(node.ParentId))
                {
                    node.ParentId = nodeKeyMap[node.ParentId];
                }
            }

            clonedTree.ContentNodes.AddRange(sourceTree.ContentNodes);

            //TODO: Wrap in transaction to ensure data integrity
            _connectDb.ContentTrees.Add(clonedTree);

            await _connectDb.SaveChangesAsync();

            return(clonedTree);
        }