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

Allow script modification before execution #3

Merged
merged 2 commits into from
Sep 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 63 additions & 11 deletions Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,39 @@ public class Database
{
static readonly Regex goEx = new Regex(@"^\s*go\s*$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);

readonly string connectionString;
readonly Assembly ass;
readonly string[] createSql;
readonly string[] clearSql;

public Database(Type scriptType)
public string ConnectionString => connectionString;

public Database(string connectionString, Type scriptType)
{
if (String.IsNullOrWhiteSpace(connectionString))
throw new ArgumentNullException(nameof(connectionString));

if (scriptType == null)
throw new ArgumentNullException(nameof(scriptType));

this.connectionString = connectionString;
ass = scriptType.GetTypeInfo().Assembly;
createSql = ParseScript(ReadScript(scriptType.Namespace, "create"));
clearSql = ParseScript(ReadScript(scriptType.Namespace, "clear"));
}

public Database(Assembly scriptAss, string scriptNamespace)
public Database(string connectionString, Assembly scriptAss, string scriptNamespace)
{
if (String.IsNullOrWhiteSpace(connectionString))
throw new ArgumentNullException(nameof(connectionString));

if (scriptAss == null)
throw new ArgumentNullException(nameof(scriptAss));

if (String.IsNullOrWhiteSpace(scriptNamespace))
throw new ArgumentNullException(nameof(scriptNamespace));

this.connectionString = connectionString;
ass = scriptAss;
createSql = ParseScript(ReadScript(scriptNamespace, "create"));
clearSql = ParseScript(ReadScript(scriptNamespace, "clear"));
Expand Down Expand Up @@ -65,7 +76,7 @@ string[] ParseScript(string script)
return goEx.Split(script).Where(c => !goEx.IsMatch(c) && !String.IsNullOrWhiteSpace(c)).ToArray();
}

public bool Exists(string connectionString)
public bool Exists()
{
var builder = new SqlConnectionStringBuilder(connectionString);

Expand All @@ -81,7 +92,12 @@ public bool Exists(string connectionString)
return count > 0;
}

public void Rebuild(string connectionString)
public void Rebuild()
{
Rebuild(null);
}

public void Rebuild(Func<string, string> modifyScript)
{
var builder = new SqlConnectionStringBuilder(connectionString);

Expand All @@ -99,10 +115,15 @@ public void Rebuild(string connectionString)
));
}

Build(connectionString);
Build(modifyScript);
}

public void Build()
{
Build(null);
}

public void Build(string connectionString)
public void Build(Func<string, string> modifyScript)
{
var builder = new SqlConnectionStringBuilder(connectionString);

Expand All @@ -116,37 +137,68 @@ public void Build(string connectionString)

using (var conn = new SqlConnection(connectionString))
{
BuildSchema(conn);
BuildSchema(conn, modifyScript);
}
}

public void BuildSchema(IDbConnection conn)
{
BuildSchema(conn, null);
}

public void BuildSchema(IDbConnection conn, Func<string, string> modifyScript)
{
conn.EnsureOpen();

foreach (string statement in createSql)
conn.Execute(statement);
ExecuteScript(conn, statement, modifyScript);
}

public void Clear(string connectionString)
public void Clear()
{
Clear((Func<string, string>)null);
}

public void Clear(Func<string, string> modifyScript)
{
using (var conn = new SqlConnection(connectionString))
{
Clear(conn);
Clear(conn, modifyScript);
}
}

public void Clear(IDbConnection conn)
{
Clear(conn, null);
}

public void Clear(IDbConnection conn, Func<string, string> modifyScript)
{
conn.EnsureOpen();

using (var tx = conn.BeginTransaction())
{
foreach (string statement in clearSql)
conn.Execute(statement, transaction: tx);
ExecuteScript(conn, statement, modifyScript, tx);

tx.Commit();
}
}

void ExecuteScript(IDbConnection conn, string sql, Func<string, string> modify, IDbTransaction tx = null)
{
if (modify != null)
{
string newStatement = modify(sql);
if (!String.IsNullOrWhiteSpace(newStatement))
{
conn.Execute(newStatement, transaction: tx);
}
}
else
{
conn.Execute(sql, transaction: tx);
}
}
}
}
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@ dotnet add package Archon.Data
You should have a `create.sql` & a `clear.sql` as embedded resources in your class library. The `create.sql` script should only create tables & schemas and whatnot. It shouldn't try to create the database. The `clear.sql` script should simply clear the tables. Don't drop or create tables in the `clear.sql` script.

```cs
var db = new Database(typeof(MyType)); //where MyType is in the same namespace as your create.sql & clear.sql
string connectionString = "server=.\\sqlexpress;database=mydb;integrated security=true;";
var db = new Database(connectionString, typeof(MyType)); //where MyType is in the same namespace as your create.sql & clear.sql
```

or

```cs
var db = new Database(typeof(MyType).Assembly, "MyAssembly.Weird.Namespace.Folder1"); //specify what namespace the embedded create & clear sql scripts are in
string connectionString = "server=.\\sqlexpress;database=mydb;integrated security=true;";
var db = new Database(connectionString, typeof(MyType).Assembly, "MyAssembly.Weird.Namespace.Folder1"); //specify what namespace the embedded create & clear sql scripts are in
```

### Check if Database Exists

To check if a database exists:

```cs
bool exists = db.Exists("server=.\\sqlexpress;database=mydb;integrated security=true;");
bool exists = db.Exists();
```

It will return `true` or `false` depending on if the database in the connection string exists or not.
Expand All @@ -39,20 +41,26 @@ It will return `true` or `false` depending on if the database in the connection
To build a new database using the `create.sql` script:

```cs
db.Build("server=.\\sqlexpress;database=mydb;integrated security=true;");
db.Build();
```

This will create the database if it doesn't already exist and then run the `create.sql` script against it.
This will create the database if it doesn't already exist and then run the `create.sql` script against it. You can also use the overload which takes a `modifyScript` delegate to modify each script before it is executed (e.g. to replace tokens based on environment):

```cs
db.Build(sql => sql.Replace("{schema_name}", Thread.CurrentThread.Name));
```

Note: if the delegate returns `null` for a particular script, that script will not be run.

### Rebuild a Database

To drop an existing database and recreate it using the `create.sql` script:

```cs
db.Rebuild("server=.\\sqlexpress;database=mydb;integrated security=true;");
db.Rebuild();
```

This will drop the database if it exists and then recreate it running the `create.sql` script against it.
This will drop the database if it exists and then recreate it running the `create.sql` script against it. This method also has an overload which accepts a a `modifyScript` delegate.

### Build the Schema Only

Expand All @@ -62,17 +70,17 @@ To only run the `create.sql` script with an existing `IDbConnection`:
db.BuildSchema(myConn);
```

The connection will be opened if it is not already opened and then the `create.sql` script will be run.
The connection will be opened if it is not already opened and then the `create.sql` script will be run. This method also has an overload which accepts a a `modifyScript` delegate.

### Clear the Database

To run the `clear.sql` against an existing database:

```cs
db.Clear(myConn); //you can also pass a connection string here
db.Clear(myConn); //you can also not pass a connection object to run against the original connection string
```

This will open the connection if it is not already open (or create a connection from the connection string) and then run the `clear.sql` script. This will fail if the database does not exist.
This will open the connection if it is not already open (or create a connection from the connection string) and then run the `clear.sql` script. This will fail if the database does not exist. This method also has an overload which accepts a a `modifyScript` delegate.

### `DataContext`

Expand Down