Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for scripts in file system #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions GlobalAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en-US")]

[assembly: AssemblyVersion("1.0.3.5")]
[assembly: AssemblyFileVersion("1.0.3.5")]
[assembly: AssemblyInformationalVersion("1.0.3.5")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0.0")]
7 changes: 5 additions & 2 deletions Improving.DbUp.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio Version 16
VisualStudioVersion = 16.0.28407.52
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{6A5A7337-11EB-4B20-8B60-9FBF234019D4}"
ProjectSection(SolutionItems) = preProject
Expand Down Expand Up @@ -37,4 +37,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {33A587EE-C169-4045-AC85-C3C41449780B}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>Improving.DbUp.QuickStart</id>
<id>Improving.DbUp.FileSystem.QuickStart</id>
<version>$version$</version>
<title>$title$</title>
<authors>Craig Neuwirt,Michael Dudley,Cori Drew</authors>
<authors>Craig Neuwirt,Michael Dudley,Cori Drew,Rumit Parakhiya</authors>
<owners>Improving</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
Expand Down
4 changes: 3 additions & 1 deletion Source/Improving.DbUp.QuickStart/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ internal class Program
var shouldSeedData = env == Env.LOCAL;

var dbName = ConfigurationManager.AppSettings["DbName"];
var dbUpdater = new DbUpdater(Assembly.GetExecutingAssembly(), "Scripts", dbName, connectionStringName, scriptVariables, shouldSeedData, env);
var dbUpdater = DbUpdater.UsingEmbeddedScripts(Assembly.GetExecutingAssembly(), "Scripts", dbName, connectionStringName, scriptVariables, shouldSeedData, env);
// Or
// var dbUpdater = DbUpdater.UsingScriptsInFileSystem(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Scripts"), dbName, connectionStringName, scriptVariables, null, shouldSeedData, env)
return dbUpdater.Run() ? 0 : -1;
}
}
Expand Down
151 changes: 98 additions & 53 deletions Source/Improving.DbUp/DbUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,94 @@
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Reflection;
using DbUp;
using DbUp.Builder;
using DbUp.Engine;
using DbUp.Engine.Output;
using DbUp.Helpers;
using DbUp.ScriptProviders;
using DbUp.SqlServer;
using Improving.DbUp.Hashed;

namespace Improving.DbUp
{
using global::DbUp.Engine.Output;
using global::DbUp.Support.SqlServer;
using Hashed;

public class DbUpdater
public abstract class DbUpdater
{
private readonly string _namespacePrefix;
private readonly Assembly _migrationAssembly;
private readonly bool _seedData;
private readonly Env _env;
private readonly IUpgradeLog _upgradeLog;

public virtual string FolderName { get; }
public virtual string DatabaseName { get; }
public virtual string ConnectionString { get; }
public virtual bool UseTransactions { get; set; }
public virtual IDictionary<string, string> ScriptVariables { get; }

public DbUpdater(Assembly migrationAssembly,
protected abstract string RootPrefix { get; }
protected abstract string PathSeparator { get; }

public DbUpdater(
string folderName,
string databaseName,
string connectionStringName,
IDictionary<string, string> scriptVariables,
bool SeedData = false,
Env env = Env.Undefined)
bool seedData = false,
Env env = Env.Undefined,
IUpgradeLog upgradeLog = null)
{
_namespacePrefix = migrationAssembly.GetName().Name + ".";
_migrationAssembly = migrationAssembly;
_seedData = SeedData;
_env = env;
_seedData = seedData;
_env = env;

_upgradeLog = upgradeLog;
if (_upgradeLog == null)
{
_upgradeLog = new ConsoleUpgradeLog();
}

ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString + ";App=" + databaseName + "Migrations";
UseTransactions = true;
DatabaseName = databaseName;
FolderName = folderName;
ScriptVariables = scriptVariables;
UseTransactions = true;
DatabaseName = databaseName;
FolderName = folderName;
ScriptVariables = scriptVariables;
}

public string FolderName { get; }
public string DatabaseName { get; }
public string ConnectionString { get; }
public bool UseTransactions { get; set; }
public IDictionary<string, string> ScriptVariables { get; }

public bool Run()
{
try
{
if (IsNew())
{
Console.WriteLine("No '{0}' database found. One will be created.", DatabaseName);
_upgradeLog.WriteInformation("No '{0}' database found. One will be created.", DatabaseName);
CreateDatabase();
}

Console.WriteLine("Performing beforeMigrations for '{0}' database.", DatabaseName);
_upgradeLog.WriteInformation("Performing beforeMigrations for '{0}' database.", DatabaseName);
BeforeMigration();

Console.WriteLine("Performing migrations for '{0}' database.", DatabaseName);
_upgradeLog.WriteInformation("Performing migrations for '{0}' database.", DatabaseName);
MigrateDatabase();

Console.WriteLine("Executing hashed scripts for '{0}' database.", DatabaseName);
_upgradeLog.WriteInformation("Executing hashed scripts for '{0}' database.", DatabaseName);
HashedScripts();

Console.WriteLine("Executing always run scripts for '{0}' database.", DatabaseName);
_upgradeLog.WriteInformation("Executing always run scripts for '{0}' database.", DatabaseName);
AlwaysRun();

Console.WriteLine("Executing test scripts for '{0}' database. These are run every time.", DatabaseName);
_upgradeLog.WriteInformation("Executing test scripts for '{0}' database. These are run every time.", DatabaseName);
UpdateTests();

if (_seedData)
{
Console.WriteLine("Executing seed scripts for '{0}' database.", DatabaseName);
_upgradeLog.WriteInformation("Executing seed scripts for '{0}' database.", DatabaseName);
SeedDatabase();
}
}
catch (Exception e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
Console.ResetColor();
_upgradeLog.WriteError(e.Message);

return false;
}
Expand All @@ -93,31 +101,31 @@ public bool Run()
private void HashedScripts()
{
ExecuteHashedDatabaseActions(
_namespacePrefix + FolderName + ".Hash",
JoinPath(RootPrefix, FolderName, "Hash"),
ConnectionString,
builder => UseTransactions ? builder.WithTransactionPerScript() : builder);
}

private void AlwaysRun()
{
ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".AlwaysRun",
JoinPath(RootPrefix, FolderName, "AlwaysRun"),
ConnectionString,
builder => builder.JournalTo(new NullJournal()));
}

private void BeforeMigration()
{
ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".BeforeMigration",
JoinPath(RootPrefix, FolderName, "BeforeMigration"),
ConnectionString,
builder => UseTransactions ? builder.WithTransactionPerScript() : builder);
}

private void MigrateDatabase()
{
ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".Migration",
JoinPath(RootPrefix, FolderName, "Migration"),
ConnectionString,
builder => UseTransactions ? builder.WithTransactionPerScript() : builder);
}
Expand All @@ -126,19 +134,19 @@ private void UpdateTests()
{
if (_env != Env.LOCAL && _env != Env.DEV && _env != Env.QA)
{
Console.WriteLine("Scripts in the test folder are only executed in LOCAL, DEV and QA");
_upgradeLog.WriteInformation("Scripts in the test folder are only executed in LOCAL, DEV and QA");
return;
}
ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".Test",
JoinPath(RootPrefix, FolderName, "Test"),
ConnectionString,
builder => builder.JournalTo(new NullJournal()));
}

private void SeedDatabase()
{
ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".Seed",
JoinPath(RootPrefix, FolderName, "Seed"),
ConnectionString,
builder => UseTransactions ? builder.WithTransactionPerScript() : builder);
}
Expand All @@ -151,7 +159,7 @@ private void CreateDatabase()
};

ExecuteDatabaseActions(
_namespacePrefix + FolderName + ".FirstRun",
JoinPath(RootPrefix, FolderName, "FirstRun"),
masterDbSqlConnection.ConnectionString,
builder => builder.JournalTo(new NullJournal()));
}
Expand All @@ -163,45 +171,42 @@ private void ExecuteDatabaseActions(string scriptPrefix, string connectionString
DeployChanges.To
.SqlDatabase(connectionString)
.WithExecutionTimeout(TimeSpan.FromSeconds(2147483647))
.WithScriptsEmbeddedInAssembly(_migrationAssembly,
name => name.StartsWith(scriptPrefix))
.WithScripts(UnderlyingScriptProvider(scriptPrefix))
.WithVariables(ScriptVariables)
.LogToConsole();

PerformUpgrade(customBuilderTransform, builder);
}

protected abstract IScriptProvider UnderlyingScriptProvider(string scriptPrefix);

private void PerformUpgrade(Func<UpgradeEngineBuilder, UpgradeEngineBuilder> customBuilderTransform, UpgradeEngineBuilder builder)
{
var upgrader = customBuilderTransform(builder).Build();
var createResult = upgrader.PerformUpgrade();

if (!createResult.Successful)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(createResult.Error);
_upgradeLog.WriteError(createResult.Error.Message);
throw createResult.Error;
}
else
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Success!");
Console.ResetColor();
_upgradeLog.WriteInformation("Success!");
}
}

private void ExecuteHashedDatabaseActions(string scriptPrefix, string connectionString,
Func<UpgradeEngineBuilder, UpgradeEngineBuilder> customBuilderTransform)
{
var sqlConnectionManager = new SqlConnectionManager(connectionString);
var log = new ConsoleUpgradeLog();
var journal = new HashedSqlTableJournal(() => sqlConnectionManager, () => log, null, HashedSqlServerExtensions.VersionTableName);
var journal = new HashedSqlTableJournal(() => sqlConnectionManager, () => _upgradeLog, null, HashedSqlServerExtensions.VersionTableName);

var builder =
DeployChanges.To
.HashedSqlDatabase(sqlConnectionManager)
.WithExecutionTimeout(TimeSpan.FromSeconds(2147483647))
.WithHashedScriptsEmbeddedInAssembly(_migrationAssembly, name => name.StartsWith(scriptPrefix), journal)
.WithHashedScripts(UnderlyingScriptProvider(scriptPrefix), journal)
.WithVariables(ScriptVariables)
.LogToConsole();

Expand Down Expand Up @@ -230,5 +235,45 @@ private bool IsNew()
}
}
}

private string JoinPath(params string[] values)
{
return string.Join(PathSeparator, values.Where(p => !String.IsNullOrWhiteSpace(p?.Trim())));
}

#region Factory Methods
public static DbUpdater UsingEmbeddedScripts(Assembly migrationAssembly,
string folderName,
string databaseName,
string connectionStringName,
IDictionary<string, string> scriptVariables,
bool seedData = false,
Env env = Env.Undefined,
IUpgradeLog logger = null)
{
return new EmbeddedScriptsDbUpdater(migrationAssembly, folderName, databaseName, connectionStringName, scriptVariables, seedData, env, logger);
}

public static DbUpdater UsingScriptsInFileSystem(string folderName,
string databaseName,
string connectionStringName,
IDictionary<string, string> scriptVariables,
FileSystemScriptOptions fileSystemScriptOptions = null,
bool seedData = false,
Env env = Env.Undefined,
IUpgradeLog logger = null)
{
if (fileSystemScriptOptions == null)
{
fileSystemScriptOptions = new FileSystemScriptOptions()
{
IncludeSubDirectories = true,
Filter = name => name.EndsWith(".sql")
};
}

return new FileSystemScriptsDbUpdater(folderName, fileSystemScriptOptions, databaseName, connectionStringName, scriptVariables, seedData, env, logger);
}
#endregion
}
}
43 changes: 43 additions & 0 deletions Source/Improving.DbUp/EmbeddedScriptsDbUpdater.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using DbUp.Builder;
using DbUp.Engine;
using DbUp.Engine.Output;
using DbUp.ScriptProviders;

namespace Improving.DbUp
{
public class EmbeddedScriptsDbUpdater : DbUpdater
{
private readonly string _namespacePrefix;
private readonly Assembly _migrationAssembly;

public EmbeddedScriptsDbUpdater(Assembly migrationAssembly,
string folderName,
string databaseName,
string connectionStringName,
IDictionary<string, string> scriptVariables,
bool seedData = false,
Env env = Env.Undefined,
IUpgradeLog logger = null)
: base(folderName, databaseName, connectionStringName, scriptVariables, seedData, env, logger)
{
_namespacePrefix = migrationAssembly.GetName().Name;
_migrationAssembly = migrationAssembly;
}

protected override string RootPrefix => _namespacePrefix;

protected override string PathSeparator => ".";

protected override IScriptProvider UnderlyingScriptProvider(string scriptPrefix)
{
return new EmbeddedScriptProvider(_migrationAssembly, name => name.StartsWith(scriptPrefix));
}
}
}
Loading