From 1c4cd9de242d383d3712bbf1e5377417adc1b780 Mon Sep 17 00:00:00 2001 From: Chad Lee Date: Thu, 14 Sep 2017 16:18:52 -0500 Subject: [PATCH 1/2] Update Database to take connection string in ctor It was a small annoyance when using this class having to constantly respecify the same connection string for each method you call. It is now stored with the class instance. +semver:major --- Database.cs | 25 ++++++++++++++++++------- README.md | 14 ++++++++------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Database.cs b/Database.cs index ba2b359..242750c 100644 --- a/Database.cs +++ b/Database.cs @@ -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")); @@ -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); @@ -81,7 +92,7 @@ public bool Exists(string connectionString) return count > 0; } - public void Rebuild(string connectionString) + public void Rebuild() { var builder = new SqlConnectionStringBuilder(connectionString); @@ -99,10 +110,10 @@ public void Rebuild(string connectionString) )); } - Build(connectionString); + Build(); } - public void Build(string connectionString) + public void Build() { var builder = new SqlConnectionStringBuilder(connectionString); @@ -128,7 +139,7 @@ public void BuildSchema(IDbConnection conn) conn.Execute(statement); } - public void Clear(string connectionString) + public void Clear() { using (var conn = new SqlConnection(connectionString)) { diff --git a/README.md b/README.md index 0d19594..9900dd1 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,15 @@ 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 @@ -29,7 +31,7 @@ var db = new Database(typeof(MyType).Assembly, "MyAssembly.Weird.Namespace.Folde 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. @@ -39,7 +41,7 @@ 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. @@ -49,7 +51,7 @@ This will create the database if it doesn't already exist and then run the `crea 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. @@ -69,7 +71,7 @@ The connection will be opened if it is not already opened and then the `create.s 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. From 0ebdb75db3e9d03716e4f740dc8582eef3a3e9b9 Mon Sep 17 00:00:00 2001 From: Chad Lee Date: Thu, 14 Sep 2017 16:35:05 -0500 Subject: [PATCH 2/2] Allow script modification before execution Add feature to Database to allow modification of creation/clear scripts before they are executed against the database. This is useful to replace tokens in scripts based on a current context. +semver:minor --- Database.cs | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- README.md | 14 ++++++++++---- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/Database.cs b/Database.cs index 242750c..92e9643 100644 --- a/Database.cs +++ b/Database.cs @@ -93,6 +93,11 @@ public bool Exists() } public void Rebuild() + { + Rebuild(null); + } + + public void Rebuild(Func modifyScript) { var builder = new SqlConnectionStringBuilder(connectionString); @@ -110,10 +115,15 @@ public void Rebuild() )); } - Build(); + Build(modifyScript); } public void Build() + { + Build(null); + } + + public void Build(Func modifyScript) { var builder = new SqlConnectionStringBuilder(connectionString); @@ -127,37 +137,68 @@ public void Build() 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 modifyScript) { conn.EnsureOpen(); foreach (string statement in createSql) - conn.Execute(statement); + ExecuteScript(conn, statement, modifyScript); } public void Clear() + { + Clear((Func)null); + } + + public void Clear(Func 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 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 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); + } + } } } \ No newline at end of file diff --git a/README.md b/README.md index 9900dd1..7721344 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,13 @@ To build a new database using the `create.sql` script: 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 @@ -54,7 +60,7 @@ To drop an existing database and recreate it using the `create.sql` script: 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 @@ -64,7 +70,7 @@ 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 @@ -74,7 +80,7 @@ To run the `clear.sql` against an existing database: 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`