using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using DatabaseMigrator.Properties; using DTS.Common.Storage; using Microsoft.Win32; using DTS.Common.Enums; namespace DatabaseMigrator { public partial class MigrationForm : Form { public MigrationForm(string targetDir) { InitializeComponent(); _targetDir = targetDir; } private readonly string _targetDir; private int _desiredDatabaseVersion; private bool localDataExists = false; private bool localSQLiteDataExists = false; private bool localSQLLocalDbDataExists = false; private void buttonOK_Click(object sender, EventArgs e) { SetMigrationStatus("Querying database version..."); var currentDatabaseVersion = GetDatabaseVersion(true); //fix this rbLocal.Checked); if (currentDatabaseVersion == _desiredDatabaseVersion) { MessageBox.Show(string.Format(Settings.Default.SameVersionDatabase, currentDatabaseVersion), Settings.Default.ActionRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } else if (currentDatabaseVersion > _desiredDatabaseVersion) { MessageBox.Show(string.Format(Settings.Default.CurrentVersionGreater, currentDatabaseVersion), Settings.Default.ActionRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } else { SetMigrationStatus("Creating a backup database prior to migration..."); //Make a backup copy CopyLocalDB(); DbOperations.Connection.DbVersion = currentDatabaseVersion; //Migrate the database SetMigrationStatus("Migrating database from verion " + currentDatabaseVersion + " to " + _desiredDatabaseVersion + "..."); try { MigrationResult result = DbOperations.Connection.UpgradeVersionsIfNeeded(_desiredDatabaseVersion, SetStatus, "DataPRO", "previousdir", "targetdir", "DataPRO.exe", "applicationSettings" /*fix this*/); //handle exceptions and display error if (result != MigrationResult.OK) { ConvertResultAndDisplay(result); } MessageBox.Show(string.Format(Settings.Default.DatabaseMigrationSucceeded, currentDatabaseVersion, _desiredDatabaseVersion), Settings.Default.ActionRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } catch (Exception) { MessageBox.Show(string.Format(Settings.Default.DatabaseMigrationFailed, Environment.NewLine + Environment.NewLine), Settings.Default.ActionRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } } Close(); } private void ConvertResultAndDisplay(MigrationResult result) { string stringResult = ConvertMigrationResultToSetting(result); MessageBox.Show(stringResult, Settings.Default.ActionRequired, MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } private string ConvertMigrationResultToSetting(MigrationResult result) { var stringResult = string.Empty; switch (result) { case MigrationResult.OK: break; case MigrationResult.ExceptionThrown: break; case MigrationResult.WarningAllowStreamingModesWasNotMigrated: stringResult = string.Format(Settings.Default.WarningAllowStreamingModesWasNotMigrated, Environment.NewLine, Environment.NewLine); break; } return stringResult; } private void buttonCancel_Click(object sender, EventArgs e) { Close(); } private string InstallDatabase() { DbOperations._usingCentralizedDB = false; DbOperations._usingMSSQL = true; DbOperations.Connection.Server = Settings.Default.LocalDbDataPROInstance; DbOperations._usingNTLMAuthentication = true; var resultString = ProcessSqlLocalDbCommand(Settings.Default.StopDataProInstance); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } resultString = ProcessSqlLocalDbCommand(Settings.Default.DeleteDataProInstance); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } resultString = ProcessSqlLocalDbCommand(Settings.Default.CreateDataProInstance); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } resultString = ProcessSqlLocalDbCommand(Settings.Default.StartDataProInstance); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } //Attach the DataPRO database var dbFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, DbOperations.Connection.DBName) + ".mdf"; var logFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, DbOperations.Connection.DBName) + "_log.ldf"; resultString = AttachDatabase(_targetDir, Settings.Default.DataPRO, dbFileName, logFileName); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } //Attach the ISO database dbFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, Settings.Default.ISO) + ".mdf"; logFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, Settings.Default.ISO) + "_log.ldf"; resultString = AttachDatabase(_targetDir, Settings.Default.ISO, dbFileName, logFileName); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } return resultString; } private void SetMigrationStatus(string migrationStatus) { //Remove the previous status SetStatus(string.Empty); MigrationStatusLabel.Text = migrationStatus; MigrationStatusLabel.Refresh(); } private void SetStatus(string status, bool output = false) { statusTextLabel.Text = status; statusTextLabel.Refresh(); } /// /// Builds the path to the database and database log files and calls CopyDatabaseFile for each. /// private bool CopyLocalDB() { var dbFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text.Trim()*/ + Settings.Default.Mdf); var dbFileLogName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text.Trim()*/ + Settings.Default.LogLdf); //Copy .mdf file if (CopyDatabaseFile(Settings.Default.Mdf, dbFileName)) { //Copy .ldf file CopyDatabaseFile(Settings.Default.LogLdf, dbFileLogName); return true; } return false; } private bool CopyDatabaseFile(string extension, string destFileName) { //The database file that is to be copied was attached to (localdb)\DataPROInstance, so detach it so it can be copied var resultString = ProcessSqlLocalDbCommand(Settings.Default.StopDataProInstance); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } //var sourceFileName = string.Empty; //sourceFileName = //cbCopyDataFromPrevious.Checked ? // Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text.Trim()*/ + extension); //: //Path.Combine(_targetDir, Settings.Default.LocalDbFolder, Settings.Default.DataPRO + extension); //if (File.Exists(destFileName)) //{ //Back it up var _testIDTimestamp = string.Format("{0:0000}_{1:00}_{2:00} {3:00}_{4:00}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute); var temp = destFileName.Replace(extension, "_" + _testIDTimestamp + extension); File.Copy(destFileName, temp, true); //Don't worry about overwriting a db with this timestamp //} //try //{ // File.Copy(sourceFileName, destFileName, true); // //log.WriteEntry(sourceFileName + " was copied as " + destFileName); //} //catch (Exception ex) //{ // MessageBox.Show("Previous database was not copied: " + ex.Message); // return false; //} return true; } private int GetDatabaseVersion(bool usingLocalDatabase) { var oldVersion = 0; try { DbOperations.Connection.DBName = "DataPRO"; //fix this tbDBName.Text; if (true) //fix this usingLocalDatabase) { //log.WriteEntry("Using local database"); if (localSQLLocalDbDataExists) { //log.WriteEntry("Local SQLLocalDb database exists in previous install"); var resultString = InstallDatabase(); if (resultString.Length != 0) { MessageBox.Show(resultString, Settings.Default.Warning, MessageBoxButtons.OK); //Environment.Exit((int)STARTUP_ERRORS.ISO_DB_NOT_ATTACHED); } } //else if (localSQLiteDataExists) //{ // log.WriteEntry("Local SQLite database exists in previous install"); // DbOperations._usingMSSQL = false; // oldVersion = UsePreviousDatabaseStructure(true); // return oldVersion; //} else { //log.WriteEntry("No local database exists in previous install"); } } //else //{ // log.WriteEntry("Using centralized database"); // DbOperations._usingMSSQL = true; // DbOperations.Connection.Server = tbDBHostname.Text; // DbOperations._usingNTLMAuthentication = cbUseNTLMAuthentication.Checked; // DbOperations.Connection.Username = tbDBUser.Text; // DbOperations.Connection.Password = tbDBPassword.Text; // } //First try the most recent database structure using (var cmd = DbOperations.GetSQLCommand(true)) { try { cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_DbVersionGet.ToString(); cmd.Parameters.Add(new SqlParameter("@Version", SqlDbType.Int) { Value = null }); var reader = cmd.ExecuteReader(); var dbVersionsList = new List(); while (reader.Read()) { var version = Convert.ToInt32(reader[DbOperations.DbVersions.DbVersionFields.Version.ToString()]); dbVersionsList.Add(version); } reader.Close(); oldVersion = dbVersionsList.Max(); //log.WriteEntry("Result of using stored procedure sp_DbVersionGet is " + oldVersion); if (oldVersion > 0) { return oldVersion; } } finally { cmd.Connection.Dispose(); } } } catch (Exception) { //log.WriteEntry("Exception while getting previous database version: " + ex.Message); } return 0; //fix thisUsePreviousDatabaseStructure(usingLocalDatabase); } /// /// Get the path to the latest version of SQL Server Express LocalDB /// installed, and run the command passed in. /// /// /// private static string ProcessSqlLocalDbCommand(string command) { //SQL Server Express LocalDB 2014 is a Prerequisite of the DataPRO Installer, //so it should be there unless it has been subsequently uninstalled. var localDbPath = GetSqlServerLocalDBPath(); if (localDbPath == string.Empty) { //SQL Server LocalDb is not installed so display error and go away return Settings.Default.SqlServerLocalDbNotInstalled; } var sqlLocalDbExeFileName = localDbPath + Settings.Default.SqlLocalDBExe; return SqlCommandProcessor(sqlLocalDbExeFileName, command); } private static string AttachDatabase(string targetDir, string dbName, string sqlDbFileName, string sqlLogFileName) { const string SqlCmdExe = "sqlcmd.exe"; var oDBCToolsPath = DTS.Common.Utils.Database.GetODBCToolsPath(null); var fullSqlcmdPath = Path.Combine(oDBCToolsPath, SqlCmdExe); //e.g. $"\"C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\110\\Tools\\Binn\\sqlcmd.exe\"" //var batchFileName = Path.Combine(targetDir, Settings.Default.ScriptsFolder, Settings.Default.AttachDBsbat); var batchFileName = Settings.Default.AttachDBsbat; return BatchCommandProcessor(batchFileName, dbName, sqlDbFileName, sqlLogFileName, fullSqlcmdPath); } private static string GetSqlServerLocalDBPath() { var highestVersionInstalledPath = string.Empty; var rk = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); var sk1 = rk.OpenSubKey(Settings.Default.RegistrySoftwareMicrosoftMicrosoftSQLServerLocalDBInstalledVersions); if (sk1 == null) return string.Empty; var maxProductVersion = 0.0; foreach (var productSubKeyName in sk1.GetSubKeyNames()) { var thisVersion = -1D; if (!double.TryParse(productSubKeyName, out thisVersion)) continue; if (thisVersion < maxProductVersion) continue; maxProductVersion = thisVersion; var newKey = sk1.OpenSubKey(productSubKeyName); if (newKey == null) continue; var val = newKey.GetValue(Settings.Default.InstanceAPIPath, -1, RegistryValueOptions.None).ToString(); if ((val == "-1") || (!val.EndsWith(Settings.Default.SqlUserInstanceDll))) continue; highestVersionInstalledPath = val.Substring(0, val.Length - Settings.Default.SqlUserInstanceDll.Length); } return highestVersionInstalledPath.Replace(Settings.Default.LocalDB, Settings.Default.Tools); } private static readonly StringBuilder sb = new StringBuilder(); static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { if (outLine.Data != null) { if (string.IsNullOrWhiteSpace(outLine.Data)) { sb.Append("\r\n"); } sb.Append(outLine.Data); } } public static string SqlCommandProcessor(string sqlLocalDbExeFileName, string command) { var resultString = string.Empty; sb.Clear(); var process = new Process { StartInfo = { FileName = sqlLocalDbExeFileName, Arguments = command, LoadUserProfile = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; //* Set ONLY ONE handler here. process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); //* Start process process.Start(); //* Read one element asynchronously process.BeginErrorReadLine(); //* Read the other one synchronously var output = process.StandardOutput.ReadToEnd(); Console.WriteLine(output); //APILogger.Log("Result of " + command + " command is: " + output); process.WaitForExit(); if (sb.Length > 0) { resultString = sb.ToString(); } return resultString; } public static string BatchCommandProcessor(string batchFileName, string dbName, string sqlDbFileName, string sqlLogFileName, string fullSqlcmdPath) { var resultString = string.Empty; sb.Clear(); var process = new Process { StartInfo = { FileName = batchFileName, Arguments = dbName + " " + "\"" + sqlDbFileName + "\"" + " " + "\"" + sqlLogFileName + "\"" + " " + "\"" + fullSqlcmdPath + "\"", LoadUserProfile = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; //* Set ONLY ONE handler here. process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); //* Start process process.Start(); //* Read one element asynchronously process.BeginErrorReadLine(); //* Read the other one synchronously var output = process.StandardOutput.ReadToEnd(); Console.WriteLine(output); //APILogger.Log("Result of attach " + dbName + " using " + sqlDbFileName + " and " + sqlLogFileName + " is:"); //APILogger.Log(output); process.WaitForExit(); if (sb.Length > 0) { resultString = sb.ToString(); } return resultString; } private bool LocalDataExists() { //if (!PreviousVersionInstalled()) //{ // return false; //} //Check for "DataPRO.mdf" (SqlLocalDb in 1.5 or later) in previously-installed folder var sourceFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text*/ + Settings.Default.Mdf); if (File.Exists(sourceFileName)) { localSQLLocalDbDataExists = true; return true; } else { localSQLLocalDbDataExists = false; //Check for "datapro.db" (SQLite file in 1.4 or earlier) in previously-installed folder sourceFileName = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text*/ + Settings.Default.Db); if (File.Exists(sourceFileName)) { localSQLiteDataExists = true; return true; } else { localSQLiteDataExists = false; return false; } } } private void MigrationForm_Load(object sender, EventArgs e) { numericUpDownDesiredVersion.Minimum = 61; numericUpDownDesiredVersion.Maximum = DbOperations.CURRENT_DB_VERSION; numericUpDownDesiredVersion.Value = DbOperations.CURRENT_DB_VERSION; localDataExists = LocalDataExists(); if (localSQLLocalDbDataExists) { TbDatabasePath.Text = Path.Combine(_targetDir, Settings.Default.LocalDbFolder, "DataPRO" /*tbDBName.Text*/ + Settings.Default.Mdf); } } private void numericUpDownDesiredVersion_ValueChanged(object sender, EventArgs e) { _desiredDatabaseVersion = Convert.ToInt32(numericUpDownDesiredVersion.Value); } } }