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