/// <summary>
        /// Add the steps of an upgrade path to the dependency graph.
        /// </summary>
        /// <param name="endUpgradePathNode">The <see cref="UpgradePathNode"/> of the last step of the upgrade path.
        /// </param>
        /// <param name="databaseName">The name of the database to be upgraded.</param>
        /// <returns>The <see cref="DependencyNode"/> of the last step of the upgrade path.</returns>
        private DependencyNode AddUpgradePathToDependencyGraph(UpgradePathNode endUpgradePathNode, string databaseName)
        {
            var versions = new Stack <string>();

            DependencyNode lastDependencyNode = null;
            DependencyNode nextDependencyNode = null;

            var versionNodePairs = new List <VersionNodePair>();

            var currentUpgradePathNode = endUpgradePathNode;

            while (currentUpgradePathNode != null)
            {
                var currentNodeVersions = currentUpgradePathNode.StepGroup.GetShortDescriptions();

                foreach (var version in currentNodeVersions)
                {
                    versions.Push(version);
                }

                var currentDependencyNode = new DependencyNode(currentUpgradePathNode.StepGroup);

                if (lastDependencyNode == null)
                {
                    lastDependencyNode = currentDependencyNode;
                }

                Nodes.Add(currentDependencyNode);

                if (currentDependencyNode.StepGroup.TargetVersion != null)
                {
                    versionNodePairs.Add(
                        new VersionNodePair(currentDependencyNode.StepGroup.TargetVersion, currentDependencyNode));
                }

                if (nextDependencyNode != null)
                {
                    nextDependencyNode.Dependencies.Add(currentDependencyNode);
                }

                nextDependencyNode     = currentDependencyNode;
                currentUpgradePathNode = currentUpgradePathNode.PreviousNode;
            }

            DeploymentTransaction.Current.Log("Upgrade path for {0}: {1}", databaseName, String.Join(" -> ", versions));

            versionNodePairs.Sort();
            _databaseVersionLookupTable[databaseName] = versionNodePairs;

            return(lastDependencyNode);
        }
        /// <summary>
        /// Create an instance of the <see cref="UpgradePathGraph"/> class based on scripts in directories.
        /// </summary>
        /// <param name="databaseName">Name of the database to be upgraded.</param>
        /// <param name="sourceVersion">Installed version of the database or null if the database is not installed.
        /// </param>
        /// <param name="existingSchemaNames">The name of the schemas associated with the installed version of the logical
        /// database.</param>
        /// <param name="paths">Paths of the directories containing the upgrade scripts.</param>
        /// <param name="insertTestData">True if test data is to be inserted along with running the upgrade scripts.
        /// </param>
        public UpgradePathGraph(
            string databaseName,
            DatabaseVersion sourceVersion,
            IEnumerable <string> existingSchemaNames,
            IEnumerable <string> paths,
            bool insertTestData)
        {
            if (databaseName == null)
            {
                throw new ArgumentNullException("databaseName");
            }

            // ReSharper disable PossibleMultipleEnumeration
            if (paths == null || !paths.Any() || paths.Any(String.IsNullOrEmpty))
            {
                throw new ArgumentNullException("paths");
            }

            _databaseName = databaseName;

            var startStepGroup = new StepGroup(databaseName, sourceVersion)
            {
                SchemaNames = new HashSet <string>(existingSchemaNames, StringComparer.InvariantCultureIgnoreCase)
            };

            // ReSharper disable once ObjectCreationAsStatement
            new InitialStep(startStepGroup);
            StartNode = new UpgradePathNode(startStepGroup);

            Nodes = new List <UpgradePathNode> {
                StartNode
            };

            var nodesToProcess = new Stack <UpgradePathNode>();

            nodesToProcess.Push(StartNode);

            while (nodesToProcess.Count > 0)
            {
                var currentNode = nodesToProcess.Pop();

                IEnumerable <UpgradePathNode> nextNodes;

                if (currentNode.StepGroup.TargetVersion == null)
                {
                    nextNodes = GetCreateNodes(databaseName, paths, insertTestData);
                }
                else
                {
                    nextNodes = GetUpgradeNodes(
                        databaseName,
                        currentNode.StepGroup.TargetVersion,
                        currentNode.StepGroup.SchemaNames,
                        paths,
                        insertTestData);
                }

                foreach (var nextNode in nextNodes)
                {
                    Nodes.Add(nextNode);
                    currentNode.NextNodes.Add(nextNode);
                    nodesToProcess.Push(nextNode);
                }
            }
            // ReSharper restore PossibleMultipleEnumeration
        }
        /// <summary>
        /// Find the optimal upgrade path from the start node to the specified version.
        /// </summary>
        /// <param name="targetVersion">The target version to upgrade to.</param>
        /// <returns>The <see cref="UpgradePathNode"/> of the last step of the optimal upgrade path.</returns>
        /// <remarks>
        /// This method may only be run once.
        ///
        /// After running this method, the entire path can be determined by stepping backwards from the last node through
        /// the <see cref="UpgradePathNode.PreviousNode"/> property.
        /// </remarks>
        public UpgradePathNode FindUpgradePath(DatabaseVersion targetVersion)
        {
            if (targetVersion == null)
            {
                throw new ArgumentNullException("targetVersion");
            }

            var nodesToProcess = new Queue <UpgradePathNode>();
            var nodesToProcessInNextIteration = new Queue <UpgradePathNode>();

            nodesToProcess.Enqueue(StartNode);

            for (var iteration = 1; ; iteration++)
            {
                UpgradePathNode currentNode = null;

                while (true)
                {
                    if (currentNode == null)
                    {
                        if (nodesToProcess.Count == 0)
                        {
                            break;
                        }

                        currentNode = nodesToProcess.Dequeue();
                    }

                    if (currentNode.IterationNumber < iteration)
                    {
                        currentNode = null;
                        continue;
                    }

                    if (currentNode.StepGroup.TargetVersion == targetVersion)
                    {
                        return(currentNode);
                    }

                    var crossBranchNodes    = new List <UpgradePathNode>();
                    var nonCrossBranchNodes = new List <UpgradePathNode>();

                    foreach (var nextNode in currentNode.NextNodes)
                    {
                        var stepGroup = nextNode.StepGroup;

                        if (stepGroup.CrossBranch)
                        {
                            if (nextNode.IterationNumber <= iteration + 1)
                            {
                                continue;
                            }
                            nextNode.IterationNumber = iteration + 1;
                            nextNode.PreviousNode    = currentNode;
                            crossBranchNodes.Add(nextNode);
                        }
                        else
                        {
                            if (nextNode.IterationNumber <= iteration)
                            {
                                continue;
                            }
                            nextNode.IterationNumber = iteration;
                            nextNode.PreviousNode    = currentNode;
                            nonCrossBranchNodes.Add(nextNode);
                        }
                    }

                    crossBranchNodes.Sort();
                    for (var i = crossBranchNodes.Count - 1; i >= 0; i--)
                    {
                        nodesToProcessInNextIteration.Enqueue(crossBranchNodes[i]);
                    }

                    nonCrossBranchNodes.Sort();
                    for (var i = nonCrossBranchNodes.Count - 2; i >= 0; i--)
                    {
                        nodesToProcess.Enqueue(nonCrossBranchNodes[i]);
                    }

                    currentNode = nonCrossBranchNodes.Count > 0
                        ? nonCrossBranchNodes[nonCrossBranchNodes.Count - 1]
                        : null;
                }

                if (nodesToProcessInNextIteration.Count == 0)
                {
                    break;
                }

                nodesToProcess = nodesToProcessInNextIteration;
                nodesToProcessInNextIteration = new Queue <UpgradePathNode>();
            }

            throw new ApplicationException(String.Format(
                                               "Cannot upgrade logical database {0} to version {1} because the necessary upgrade scripts don't exist.",
                                               _databaseName, targetVersion));
        }