public string GetConnectionString( DatabaseInfo DatabaseInfo ) { if ( !string.IsNullOrEmpty( DatabaseInfo.ConnectionStringSource ) ) { return this.GetFirstConnectionStringFromConfigFiles( DatabaseInfo.ConnectionStringSource ); } else { return this.BuildConnectionString( DatabaseInfo ); } }
public static int Main( string[] args ) { string basePath = new Uri( Assembly.GetExecutingAssembly().CodeBase ).LocalPath; string migrationFilesSourcePath = null; DatabaseInfo databaseInfo = new DatabaseInfo(); string currentAppVersion = null; string migrationScriptFile = null; bool wait = false; bool verbose = false; bool showHelp = false; var arguments = new List<ArgParameter> { new ArgParameter( "migrationFilesSourcePath", v => migrationFilesSourcePath = v.FirstOrDefault() ), new ArgParameter( "connectionStringSource", v => databaseInfo.ConnectionStringSource = v.FirstOrDefault() ), new ArgParameter( "server", "S", v => databaseInfo.Server = v.FirstOrDefault() ), new ArgParameter( "database", "d", v => databaseInfo.Database = v.FirstOrDefault() ), new ArgParameter( "username", "U", v => databaseInfo.Username = v.FirstOrDefault() ), new ArgParameter( "password", "P", v => databaseInfo.Password = v.FirstOrDefault() ), new ArgParameter( "trustedConnection", "E", v => databaseInfo.TrustedConnection = true ), new ArgParameter( "currentAppVersion", v => currentAppVersion = v.FirstOrDefault() ), new ArgParameter( "migrationScriptFile", v => migrationScriptFile = v.FirstOrDefault() ), new ArgParameter( "wait", "w", v => wait = true ), new ArgParameter( "verbose", "v", v => verbose = true ), new ArgParameter( "help", "?", v => showHelp = true ) }; int matchedArgs = CommandLineParser.ParseCommandLineArguments( args, arguments ); if ( matchedArgs != args.Length ) { showHelp = true; } if ( showHelp ) { string helpContent = CommandLineParser.GetHelpText( arguments ); Console.WriteLine( "NAnt.DbMigrations.Tasks" ); Console.Write( helpContent ); return (int)ExitReason.InvalidArguments; } ILogger logger = new ConsoleLogger(); // Lame DI solution here to avoid multiple dll dependencies for this tool IHashCalculator hashCalculator = new HashCalculator(); ISqlHelper sqlHelper = new SqlHelper(); IConfigRepository configRepository = new ConfigRepository(); IMigrationDbRepository migrationDbRepository = new MigrationDbRepository( sqlHelper ); IMigrationFileRepository migrationFileRepository = new MigrationFileRepository(); ISqlCmdHelper sqlCmdHelper = new SqlCmdHelper( sqlHelper ); IMigrationOutputFileRepository migrationOutputFileRepository = new MigrationOutputFileRepository(); IMigrationFileParser migrationFileParser = new MigrationFileParser(); IMigrationFileService migrationFileService = new MigrationFileService( hashCalculator, migrationFileRepository, migrationFileParser ); IConnectionStringService connectionStringService = new ConnectionStringService( configRepository ); IPathService pathService = new PathService(); App app = new App( logger, migrationDbRepository, migrationFileRepository, sqlCmdHelper, migrationOutputFileRepository, migrationFileService, connectionStringService, pathService ); ExitReason result = app.RunMigrations( basePath, verbose, migrationFilesSourcePath, databaseInfo, currentAppVersion, migrationScriptFile ); if ( wait ) { Console.WriteLine( "Migration complete, push any key to exit" ); Console.ReadKey(); } return (int)result; }
protected override void ExecuteTask() { ILogger logger = new ActionLogger( this.Log ); // Lame DI solution here to avoid multiple dll dependencies for this tool IHashCalculator hashCalculator = new HashCalculator(); ISqlHelper sqlHelper = new SqlHelper(); IConfigRepository configRepository = new ConfigRepository(); IMigrationDbRepository migrationDbRepository = new MigrationDbRepository( sqlHelper ); IMigrationFileRepository migrationFileRepository = new MigrationFileRepository(); ISqlCmdHelper sqlCmdHelper = new SqlCmdHelper( sqlHelper ); IMigrationOutputFileRepository migrationOutputFileRepository = new MigrationOutputFileRepository(); IMigrationFileParser migrationFileParser = new MigrationFileParser(); IMigrationFileService migrationFileService = new MigrationFileService( hashCalculator, migrationFileRepository, migrationFileParser ); IConnectionStringService connectionStringService = new ConnectionStringService( configRepository ); IPathService pathService = new PathService(); App app = new App( logger, migrationDbRepository, migrationFileRepository, sqlCmdHelper, migrationOutputFileRepository, migrationFileService, connectionStringService, pathService ); DatabaseInfo databaseInfo = new DatabaseInfo { ConnectionStringSource = this.ConnectionStringSource, Server = this.Server, Database = this.Database, Username = this.Username, Password = this.Password, TrustedConnection = this.TrustedConnection }; ExitReason result = app.RunMigrations( this.Project.BaseDirectory, this.Verbose, this.MigrationFilesSourcePath, databaseInfo, this.CurrentAppVersion, this.MigrationScriptFile ); if ( this.FailOnError && result != ExitReason.Success ) { throw new BuildException( "Error in db-migration: "+result.ToString() ); } }
private string BuildConnectionString( DatabaseInfo DatabaseInfo ) { SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); connectionStringBuilder.DataSource = DatabaseInfo.Server; connectionStringBuilder.InitialCatalog = DatabaseInfo.Database; if ( DatabaseInfo.TrustedConnection ) { connectionStringBuilder.IntegratedSecurity = true; } else { connectionStringBuilder.UserID = DatabaseInfo.Username; connectionStringBuilder.Password = DatabaseInfo.Password; } return connectionStringBuilder.ConnectionString; }
// TODO: This method is doing too much /// <param name="BaseDirectory">Normalize other paths to this directory</param> /// <param name="MigrationFilesSourcePath">Directory to pull migration files from</param> /// <param name="DatabaseInfo">Database connection details</param> /// <param name="CurrentAppVersion">The current git hash</param> /// <param name="MigrationScriptFile">The file to write the consolidated migration file into</param> /// <returns>The reason for exiting: success or failure</returns> public ExitReason RunMigrations( string BaseDirectory, bool Verbose, string MigrationFilesSourcePath, DatabaseInfo DatabaseInfo, string CurrentAppVersion, string MigrationScriptFile ) { if ( DatabaseInfo == null ) { this.logger.Log( "No connection string details sent" ); return ExitReason.NoConnectionString; } string migrationFilesSourcePath = this.pathService.NormalizePath( BaseDirectory, MigrationFilesSourcePath ); DatabaseInfo.ConnectionStringSource = this.pathService.NormalizePath( BaseDirectory, DatabaseInfo.ConnectionStringSource ); string migrationScriptFile = this.pathService.NormalizePath( BaseDirectory, MigrationScriptFile ); // Invalid config source if ( !this.migrationFileRepository.PathExists( migrationFilesSourcePath ) ) { this.logger.Log( "Migration files source path is not found" ); return ExitReason.InvalidMigrationFileSourcePath; } // Get the db connection string string connnectionString = this.connectionStringService.GetConnectionString( DatabaseInfo ); if ( string.IsNullOrEmpty( connnectionString ) ) { this.logger.Log( "No valid connection string found in the target configs" ); return ExitReason.NoConnectionString; } if ( Verbose ) { this.logger.Info( "migrationFilesSourcePath: " + migrationFilesSourcePath ); this.logger.Info( "currentAppVersion: " + CurrentAppVersion ); this.logger.Info( "migrationScriptFile: " + migrationScriptFile ); if ( !string.IsNullOrEmpty( DatabaseInfo.ConnectionStringSource ) ) { this.logger.Info( "connectionStringSource: " + DatabaseInfo.ConnectionStringSource ); } this.logger.Info( "connectionString: " + connnectionString ); } // Are both source paths valid? string connectTest = this.migrationDbRepository.TestConnection( connnectionString ); if ( !string.IsNullOrEmpty(connectTest) ) { this.logger.Log( "Unable to connect to target database: " + connectTest ); return ExitReason.InvalidDatabase; } // Ensure migration table exists // FRAGILE: Must be outside TransactionScope to query it, thus if the migrations fail, the table creation won't get rolled back this.migrationDbRepository.EnsureMigrationsTableExists( connnectionString ); // Get all migrations from both db and files List<MigrationHistory> dbMigrations = this.migrationDbRepository.GetAllMigrations( connnectionString ) ?? new List<MigrationHistory>(); List<MigrationFile> fileMigrations = this.migrationFileService.GetAllMigrations( migrationFilesSourcePath ) ?? new List<MigrationFile>(); // Decide which migrations to process List<MigrationHistory> toRemove = this.MigrationsToRemove( dbMigrations, fileMigrations ); List<MigrationFile> toAdd = this.MigrationsToAdd( fileMigrations, dbMigrations ); // The output file StringBuilder migrationScript = new StringBuilder(); // Create transaction scope using ( TransactionScope transaction = new TransactionScope() ) { try { if ( toRemove != null && toRemove.Count > 0 ) { foreach ( MigrationHistory row in toRemove ) { try { migrationScript.AppendLine( "-- Remove Migration " + row.Filename ); if ( !string.IsNullOrEmpty( row.DownScript ) ) { // Run Down Migration this.sqlCmdHelper.RunSqlCommand( connnectionString, row.DownScript ); migrationScript.AppendLine( row.DownScript ); migrationScript.AppendLine( "GO" ); } string deleteContent = this.migrationDbRepository.RemoveMigration( connnectionString, row ); migrationScript.AppendLine( deleteContent ); migrationScript.AppendLine( "GO" ); migrationScript.AppendLine(); this.logger.Info( "Successfully removed " + row.Filename ); } catch { this.logger.Log( "Error removing " + row.Filename ); throw; } } } if ( toAdd != null && toAdd.Count > 0 ) { foreach ( MigrationFile file in toAdd ) { try { // Run Migration this.sqlCmdHelper.RunSqlCommand( connnectionString, file.FileContent ); // Log that migration was run string addContent = this.migrationDbRepository.AddMigration( connnectionString, new MigrationHistory { Filename = file.Filename, FileHash = file.FileHash, ExecutionDate = DateTime.Now, Version = CurrentAppVersion, DownScript = file.DownScript, } ); migrationScript.AppendLine( "-- Add Migration " + file.Filename ); migrationScript.AppendLine( file.FileContent ); migrationScript.AppendLine( "GO" ); migrationScript.AppendLine( addContent ); migrationScript.AppendLine( "GO" ); migrationScript.AppendLine(); this.logger.Info( "Successfully added " + file.Filename ); } catch { this.logger.Log( "Error adding " + file.Filename ); throw; } } } transaction.Complete(); // Success } catch ( Exception ex ) { transaction.Dispose(); // Fail this.logger.Log( ex ); return ExitReason.DatabaseError; // Probably } } string migrationScriptContent = migrationScript.ToString(); if ( !string.IsNullOrEmpty( migrationScriptFile ) ) { // Create migration output script file // FRAGILE: ASSUME: it'll run the same as running them individually this.migrationOutputFileRepository.WriteFile( migrationScriptFile, migrationScriptContent ); } return ExitReason.Success; }