/// <summary> /// Creates a new database. /// </summary> /// <param name="info">Information describing the database to affect.</param> /// <param name="databaseUserNamePublic">Name of a public database user to create.</param> /// <param name="databasePasswordPublic">The database password for the new public user.</param> /// <param name="scriptResourceNames">A list of the names of the resource files containing the DDL scripts.</param> /// <param name="scriptLoader">Class that can load assemblies containing SQL script resource files.</param> /// <param name="creatorCredentials">The credentials used to create the database.</param> /// <returns> /// An instance of the <see cref="ServiceResponse"/> class. /// </returns> public ServiceResponse CreateDatabase(DatabaseInfo info, string databaseUserNamePublic, string databasePasswordPublic, IEnumerable<string> scriptResourceNames, ScriptLoader scriptLoader, DatabaseCredentials creatorCredentials) { return CreateDatabase(info, databaseUserNamePublic, databasePasswordPublic, null, scriptResourceNames, scriptLoader, creatorCredentials); }
private ServiceResponse CreateDatabase(DatabaseInfo info, string databaseUserNamePublic, string databasePasswordPublic, string scriptResourceName, IEnumerable<string> scriptResourceNames, ScriptLoader scriptLoader, DatabaseCredentials creatorCredentials) { var creatorInfo = info.CloneWithNewCredentials(creatorCredentials); using (var database = new DatabaseManipulator(creatorInfo)) { try { smo.Server sqlServer = database.SqlServer; // Make sure SQL Server allows SQL logins if needed if (creatorInfo.HasUserName()) { if (database.LoginMode == smo.ServerLoginMode.Integrated) { _logger.Error("Database Server is not configured for mixed-mode authentication.", "Database Server", creatorInfo.ServerName); _response.AddError(string.Format(CultureInfo.InvariantCulture, "Database Server ({0}) is not configured for mixed-mode authentication.", creatorInfo.ServerName)); } } if (!_response.HasErrors) { if (!database.Exists()) { _logger.Info("Creating database.", "Database name", info.DatabaseName, "Database Server", info.ServerName); // Create new database var newDatabase = new smo.Database(sqlServer, info.DatabaseName); newDatabase.Create(); database.CreateLogin(info.UserName, info.Password, true); database.CreateLogin(databaseUserNamePublic, databasePasswordPublic, false); // I don't know why we have to dispose this here, but I was afraid to remove it database.Dispose(); UpdateDatabaseSchema(info, databaseUserNamePublic, scriptResourceName, scriptResourceNames, scriptLoader); _logger.Info("Database created successfully.", info.GenerateCustomLoggingProperties()); } else { _logger.Error("Database already exists.", info.GenerateCustomLoggingProperties()); _response.AddError(string.Format(CultureInfo.InvariantCulture, "Database ({0}) on server ({1}) already exists.", info.DatabaseName, info.ServerName)); } } } catch (Exception ex) { _logger.Error("Creation of Database failed.", ex, info.GenerateCustomLoggingProperties()); _response.AddError(string.Format(CultureInfo.InvariantCulture, "Creation of Database ({0}) on server ({1}) failed: {2}", info.DatabaseName, info.ServerName, ex.Message)); } } return _response; }
private ServiceResponse UpdateDatabaseSchema(DatabaseInfo info, string databaseUserNamePublic, string scriptResourceName, IEnumerable<string> scriptResourceNames, ScriptLoader scriptLoader) { // Create temp App Domain for loading assemblies AppDomain tempAppDomain = AppDomain.CreateDomain("TempAppDomain"); try { using (var ts = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 5, 0))) { using (var database = new DatabaseManipulator(info)) { smo.Server sqlServer = database.SqlServer; // Create the script version info table if needed var versionInfoDao = new SqlScriptVersionInfoDao(info.ConnectionString); versionInfoDao.CreateScriptVersionTable(info); _logger.Info("Verifying database schema...", "Database Name", info.DatabaseName); // Get script execution history as a dictionary var sqlScriptVersionInfoDict = new Dictionary<string, string>(); foreach (IVersionInfo sqlScriptVersionInfo in versionInfoDao.ReadList()) { sqlScriptVersionInfoDict.Add(sqlScriptVersionInfo.Component, sqlScriptVersionInfo.Version); } // Get all assembly names in the specified directory IEnumerable<string> files = scriptLoader.GetSortedScriptAssemblyList(); foreach (string file in files) { // Delegate assembly loading/processing to temp App Domain tempAppDomain.SetData("FileName", file); tempAppDomain.SetData("ResourceFileName", scriptResourceName); tempAppDomain.SetData("ResourceFileNames", scriptResourceNames); var dbScriptsToExecute = new Dictionary<string, string>(); try { tempAppDomain.DoCallBack(new CrossAppDomainDelegate(ScriptLoader.GetScriptsFromAssembly)); dbScriptsToExecute = (Dictionary<string, string>)tempAppDomain.GetData("DbScriptsToExecute"); } catch (Exception) { _logger.Error("Error loading assembly!", "FileName", file); } // Sort the scripts by key string[] sortedScriptKeys = new string[dbScriptsToExecute.Keys.Count]; dbScriptsToExecute.Keys.CopyTo(sortedScriptKeys, 0); Array.Sort(sortedScriptKeys); foreach (string scriptKey in sortedScriptKeys) { string scriptValue = dbScriptsToExecute[scriptKey]; string[] keyParts = scriptKey.Split("_".ToCharArray()); if (keyParts.GetUpperBound(0) != 1) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Invalid script key format ({0})", scriptKey)); } string component = Path.GetFileNameWithoutExtension(file); string version = keyParts[1]; // If component has no script history, or current script hasn't been executed, execute it if (!sqlScriptVersionInfoDict.ContainsKey(component) || (int.Parse(version, CultureInfo.InvariantCulture) > int.Parse(sqlScriptVersionInfoDict[component], CultureInfo.InvariantCulture))) { ConsoleUtils.WriteLine(string.Format(CultureInfo.InvariantCulture, " Executing\n Component: {0}\n Script: {1}\n", component, scriptKey), ConsoleColor.Green); scriptValue = ApplyScriptTokens(scriptValue, databaseUserNamePublic); sqlServer.Databases[info.DatabaseName].ExecuteNonQuery(scriptValue); // Check if script version information already exists for this component IVersionInfo versionInfo = versionInfoDao.Read(component); AddOrUpdateVersionInfo(component, version, versionInfo, versionInfoDao); } } } ts.Complete(); } } } catch (Exception ex) { _logger.Error("Update of database failed. Please check that you have configured the database server to allow both SQL Server authentication and Windows authentication.", ex, info.GenerateCustomLoggingProperties()); _response.AddError("Update of database ({0}) on server ({1}) failed.", info.DatabaseName, info.ServerName); } finally { // Unload the temp App Domain, which unloads any loaded assemblies AppDomain.Unload(tempAppDomain); } return _response; }
/// <summary> /// Updates the schema of the specified database by scanning for new SQL scripts /// within embedded resource files, and executing those scripts. /// </summary> /// <param name="info">The information describing the database to update.</param> /// <param name="databaseUserNamePublic">The public database user name.</param> /// <param name="scriptResourceNames">A list of the names of the resource files containing the DDL scripts.</param> /// <param name="scriptLoader">Class that can load assemblies containing SQL script resource files.</param> /// <returns> /// An instance of the <see cref="ServiceResponse"/> class. /// </returns> public ServiceResponse UpdateDatabaseSchema(DatabaseInfo info, string databaseUserNamePublic, IEnumerable<string> scriptResourceNames, ScriptLoader scriptLoader) { return UpdateDatabaseSchema(info, databaseUserNamePublic, null, scriptResourceNames, scriptLoader); }