This commit is contained in:
2025-08-31 07:32:30 -05:00
parent 8cc304c3d2
commit 0fe77cb654
7 changed files with 446 additions and 138 deletions

59
ColumnCollection.cs Normal file
View File

@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DbMigrate {
public class ColumnCollection {
public Dictionary<string, string> Items { get; private set; } = new Dictionary<string, string>();
public ColumnCollection() {
Items = new Dictionary<string, string>();
}
public string this[string columnName] {
get {
if (Items.ContainsKey(columnName)) {
return Items[columnName];
}
return null;
}
set {
if (Items.ContainsKey(columnName)) {
Items[columnName] = value;
} else {
Items.Add(columnName, value);
}
}
}
public void Add(string columnName, string columnDefinition) {
if (!Items.ContainsKey(columnName)) {
Items.Add(columnName, columnDefinition);
}
}
public bool Contains(string columnName) {
return Items.ContainsKey(columnName);
}
public string[] GetColumnNames() {
return Items.Keys.ToArray();
}
public int Count() {
return Items.Count;
}
public void Clear() {
Items.Clear();
}
public override string ToString() {
StringBuilder sb = new StringBuilder();
foreach (var kvp in Items) {
sb.AppendLine($"{kvp.Key}: {kvp.Value}");
}
return sb.ToString();
}
}
}

View File

@@ -21,8 +21,8 @@ namespace DbMigrate {
private string[] getCommonColumns(string table) { private string[] getCommonColumns(string table) {
string[] baseColumns = BaseDatabase[table].Columns.Keys.ToArray(); string[] baseColumns = BaseDatabase[table].GetColumnNames();
string[] newColumns = CompareDatabase[table].Columns.Keys.ToArray(); string[] newColumns = CompareDatabase[table].GetColumnNames();
return baseColumns.Where(f => newColumns.Contains(f)).ToArray(); return baseColumns.Where(f => newColumns.Contains(f)).ToArray();
} }
@@ -74,13 +74,13 @@ namespace DbMigrate {
} }
bool alterTableRequired = false; bool alterTableRequired = false;
foreach (var column in table.Columns) { foreach (var column in table.GetColumnNames()) {
if (!BaseDatabase[table.TableName].HasColumn(column.Key)) { if (!BaseDatabase[table.TableName].HasColumn(column)) {
// The database column does not exist // The database column does not exist
alterTableRequired = true; alterTableRequired = true;
break; break;
} }
if (BaseDatabase[table.TableName].Columns[column.Key] != column.Value) { if (BaseDatabase[table.TableName].Columns[column] != table.Columns[column]) {
// The database column exists but is different // The database column exists but is different
alterTableRequired = true; alterTableRequired = true;
break; break;

View File

@@ -64,8 +64,11 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ColumnCollection.cs" />
<Compile Include="DbComparer.cs" /> <Compile Include="DbComparer.cs" />
<Compile Include="Extensions.cs" /> <Compile Include="Extensions.cs" />
<Compile Include="Migration.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SqlDatabase.cs" /> <Compile Include="SqlDatabase.cs" />
<Compile Include="SqliteTableDefinition.cs" /> <Compile Include="SqliteTableDefinition.cs" />

16
Migration.cs Normal file
View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace DbMigrate {
public class Migration {
public string MigrationName { get; set; }
public string Sql { get; set; }
public bool Executed { get; set; } = false;
public List<Exception> Errors { get; set; } = new List<Exception>();
public Migration(string name, string sql) {
MigrationName = name;
Sql = sql;
}
}
}

195
Migrations.cs Normal file
View File

@@ -0,0 +1,195 @@
using Dapper;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace DbMigrate {
public class Migrations {
public SqlDatabase BaseDatabase { get; set; }
public SqlDatabase CompareDatabase { get; set; }
public string DatabaseName { get; private set; }
public string MigrationScriptPath { get; set; } = ".\\sql";
public bool RemoveUnusedTablesFromBaseDb { get; set; } = false;
public bool RemoveUnusedColumnsFromBaseDb { get; set; } = false;
public bool CopyDataFromNewDb { get; set; } = false;
public string DeltaSql { get; private set; }
public List<Migration> MigrationSql { get; private set; } = new List<Migration>();
public Migrations(SqlDatabase baseDb, SqlDatabase compareDb = null) {
BaseDatabase = baseDb;
CompareDatabase = compareDb;
setDatbaseName();
}
public Migrations(string baseDbConnectionString, string compareDbConnectionString = null) {
BaseDatabase = new SqlDatabase(baseDbConnectionString);
if (compareDbConnectionString != null) {
CompareDatabase = new SqlDatabase(compareDbConnectionString);
}
setDatbaseName();
}
private void setDatbaseName() {
string fullPath = Regex.Match(BaseDatabase.ConnectionString, "Data Source=(.*?);", RegexOptions.IgnoreCase)?.Groups[1]?.Value;
if (fullPath != null) {
DatabaseName = Path.GetFileNameWithoutExtension(fullPath);
}
}
public async Task PerformMigration(bool performDelta = true) {
if (!Directory.Exists(MigrationScriptPath)) {
Directory.CreateDirectory(MigrationScriptPath);
}
if (BaseDatabase == null || string.IsNullOrEmpty(BaseDatabase.ConnectionString) || string.IsNullOrEmpty(DatabaseName)) {
throw new InvalidOperationException("Base Database is not set");
}
using (SQLiteConnection cn = new SQLiteConnection(BaseDatabase.ConnectionString)) {
if (performDelta) {
// Generate the delta SQL
string deltaSql = GetDeltaSql();
// Update the MigrationSql property
MigrationSql.Add(new Migration(DatabaseName.ToLower() + "_delta_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".sql", deltaSql));
}
string[] completedMigrations = File.ReadAllLines(Path.Combine(MigrationScriptPath, "completed.txt"));
var migrationFiles = Directory.GetFiles(MigrationScriptPath, DatabaseName + "_*.sql")
.Where(f => !completedMigrations.Contains(Path.GetFileName(f)))
.OrderBy(f => f);
// Read each migration file and add it to the MigrationSql list
foreach (var file in migrationFiles) {
string sql = File.ReadAllText(file);
// Add this migration to the list
MigrationSql.Add(new Migration(Path.GetFileName(file), sql));
}
foreach (var migration in MigrationSql) {
try {
// Execute the migration
await cn.ExecuteAsync(migration.Sql);
migration.Executed = true;
// Mark this migration as completed by adding it to the completed.txt file
File.AppendAllLines(Path.Combine(MigrationScriptPath, "completed.txt"), new[] { migration.MigrationName });
// TODO : Report progress
} catch (Exception ex) {
migration.Errors.Add(ex);
}
}
}
}
public string GetDeltaSql() {
if (CompareDatabase == null) {
throw new InvalidOperationException("Compare Database is not set");
}
if (BaseDatabase == null) {
throw new InvalidOperationException("Base Database is not set");
}
var sb = new StringBuilder();
foreach (var table in CompareDatabase.Tables) {
if (!BaseDatabase.ContainsTable(table.TableName)) {
// Table does not exist; Create it
sb.AppendLine("\r\n-- Create table " + table.TableName);
sb.Append(table.CreateTableSql);
if (CopyDataFromNewDb) {
// Copy data
sb.AppendLine("\r\n-- Copy data into new table " + table.TableName);
// TODO : Copy data from existing table in the new schema
}
continue;
}
// The table exists, now we need to compare everything to verify no updates are required!
foreach (var index in table.Indexes) {
if (!BaseDatabase[table.TableName].HasIndex(index.Key)) {
sb.AppendLine("\r\n-- Create index " + index.Key);
sb.AppendLine(index.Value);
} else {
// Index exists, check if it's the same
if (BaseDatabase[table.TableName].Indexes[index.Key] != index.Value) {
sb.AppendLine("\r\n-- Drop and recreate index " + index.Key);
sb.AppendLine("DROP INDEX IF EXISTS " + index.Key + ";");
sb.AppendLine(index.Value);
}
}
}
foreach (var trigger in table.Triggers) {
if (!BaseDatabase[table.TableName].HasTrigger(trigger.Key)) {
sb.AppendLine("\r\n-- Create trigger " + trigger.Key);
sb.AppendLine(trigger.Value);
} else {
// Trigger exists, check if it's the same
if (BaseDatabase[table.TableName].Triggers[trigger.Key] != trigger.Value) {
sb.AppendLine("\r\n-- Drop and recreate trigger " + trigger.Key);
sb.AppendLine("DROP TRIGGER IF EXISTS " + trigger.Key + ";");
sb.AppendLine(trigger.Value);
}
}
}
bool alterTableRequired = false;
foreach (var column in table.GetColumnNames()) {
if (!BaseDatabase[table.TableName].HasColumn(column)) {
// The database column does not exist
alterTableRequired = true;
break;
}
if (BaseDatabase[table.TableName].Columns[column] != table.Columns[column]) {
// The database column exists but is different
alterTableRequired = true;
break;
}
}
// TODO : This deletes unused columns - we should probably make this optional
if (alterTableRequired) {
sb.AppendLine("\r\n-- Table " + table.TableName + " requires alteration - Create temp table and move data");
string[] commonColumns = getCommonColumns(table.TableName);
string columnList = string.Join(",", commonColumns);
string sql = CompareDatabase[table.TableName].CreateTableSql;
string newTableName = table.TableName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss");
sql = Regex.Replace(sql, "CREATE TABLE (\\w*)", "CREATE TABLE IF NOT EXISTS $1_" + newTableName.Substring(newTableName.IndexOf("_") + 1));
sb.AppendLine(sql);
sb.AppendLine("\r\n-- Copy data from existing table to new table");
sb.AppendLine("INSERT INTO " + newTableName + " (" + columnList + ")");
sb.AppendLine("\tSELECT " + columnList);
sb.AppendLine("\tFROM " + table.TableName + ";");
sb.AppendLine("\r\n-- Drop existing table");
sb.AppendLine("DROP TABLE " + table.TableName + ";");
sb.AppendLine("\r\n-- Rename the new table to replace the old table");
sb.AppendLine("ALTER TABLE " + newTableName + " RENAME TO " + table.TableName + ";");
}
}
DeltaSql = "BEGIN TRANSACTION;\r\n" + sb.ToString() + "\r\nCOMMIT;";
return DeltaSql;
}
private string[] getCommonColumns(string table) {
string[] baseColumns = BaseDatabase[table].GetColumnNames();
string[] newColumns = CompareDatabase[table].GetColumnNames();
return baseColumns.Where(f => newColumns.Contains(f)).ToArray();
}
}
}

View File

@@ -1,11 +1,11 @@
using Dapper; using Dapper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Data.SQLite; using System.Data.SQLite;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -16,161 +16,199 @@ namespace DbMigrate {
public List<SqlTable> Tables { get; set; } = new List<SqlTable>(); public List<SqlTable> Tables { get; set; } = new List<SqlTable>();
public SqlDatabase() { public SqlDatabase() { }
} public SqlDatabase(string connectionString) {
public void Connect(string connectionString) {
ConnectionString = connectionString; ConnectionString = connectionString;
} }
public void LoadSql(string sql) { /// <summary>
/// Loads the specified SQL script and initializes an in-memory SQLite database using the script.
/// </summary>
/// <remarks>This method creates a temporary SQLite database file, sets up a connection string,
/// and executes the provided SQL script to initialize the database. The connection string for the database is
/// stored in the <see cref="ConnectionString"/> property.</remarks>
/// <param name="sql">The SQL script to be executed for creating and populating the database.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task LoadSql(string sql) {
SqlScript = sql; SqlScript = sql;
Tables = ParseTablesFromSql(sql).ToList(); string tempDbFile = Path.GetTempFileName();
ConnectionString = "Data Source=" + tempDbFile + "; Version=3;";
BuildDbFromSql(ConnectionString);
await BuildSql(ConnectionString);
} }
public void LoadSqlFromFile(string fileName) { /// <summary>
if (!File.Exists(fileName)) { /// Determines whether a table with the specified name exists in the collection.
throw new FileNotFoundException("SQL file '" + fileName + "' was not found and could not be loaded."); /// </summary>
} /// <param name="tableName">The name of the table to search for. The comparison is case-insensitive.</param>
/// <returns><see langword="true"/> if a table with the specified name exists; otherwise, <see langword="false"/>.</returns>
string sql = File.ReadAllText(fileName);
LoadSql(sql);
}
public bool ContainsTable(string tableName) { public bool ContainsTable(string tableName) {
return Tables.Count(f => f.TableName.ToLower() == tableName.ToLower()) > 0; return Tables.Count(f => f.TableName.ToLower() == tableName.ToLower()) > 0;
} }
/// <summary>
/// Executes the provided SQL script to build or modify a database using the specified connection string.
/// </summary>
/// <remarks>This method opens a connection to the database, executes the SQL script defined in
/// the <c>SqlScript</c> field, and then closes the connection. Ensure that <c>SqlScript</c> contains valid SQL
/// commands appropriate for the database schema.</remarks>
/// <param name="dbConnectionString">The connection string used to establish a connection to the SQLite database. This must be a valid SQLite
/// connection string.</param>
public void BuildDbFromSql(string dbConnectionString) {
using (SQLiteConnection cn = new SQLiteConnection(dbConnectionString)) {
ConnectionString = dbConnectionString;
using (SQLiteCommand cmd = new SQLiteCommand(cn)) {
cmd.CommandText = SqlScript;
cmd.ExecuteNonQuery();
}
}
}
/// <summary>
/// Gets the <see cref="SqlTable"/> with the specified table name, or <c>null</c> if no matching table is found.
/// </summary>
/// <param name="tableName">The name of the table to retrieve. The comparison is case-insensitive.</param>
/// <returns></returns>
public SqlTable this[string tableName] { public SqlTable this[string tableName] {
get { get {
return Tables.FirstOrDefault(f => f.TableName.ToLower() == tableName.ToLower()); return Tables.FirstOrDefault(f => f.TableName.ToLower() == tableName.ToLower());
} }
} }
public IEnumerable<SqlTable> ParseTablesFromSql(string sql) { //public IEnumerable<SqlTable> ParseTablesFromSql(string sql) {
SqlTable table = null; // SqlTable table = null;
StringBuilder sb = new StringBuilder(); // StringBuilder sb = new StringBuilder();
Dictionary<string, List<string>> indexes = new Dictionary<string, List<string>>(); // Dictionary<string, List<string>> indexes = new Dictionary<string, List<string>>();
Dictionary<string, List<string>> triggers = new Dictionary<string, List<string>>(); // Dictionary<string, List<string>> triggers = new Dictionary<string, List<string>>();
string currentElementType = ""; // string currentElementType = "";
foreach (string line in Regex.Split(sql, "\\r\\n")) { // foreach (string line in Regex.Split(sql, "\\r\\n")) {
if (string.IsNullOrEmpty(line) || line.StartsWith("--")) { // if (string.IsNullOrEmpty(line) || line.StartsWith("--")) {
continue; // continue;
} // }
string trimmedLine = line; // string trimmedLine = line;
if (currentElementType == "trigger" || currentElementType == "index") { // if (currentElementType == "trigger" || currentElementType == "index") {
trimmedLine = line.Trim(); // trimmedLine = line.Trim();
} else { // } else {
trimmedLine = line.StartsWith(" ") ? "&&" + line.Trim() : line; // trimmedLine = line.StartsWith(" ") ? "&&" + line.Trim() : line;
trimmedLine = Regex.Replace(trimmedLine, @"\s+", " "); // trimmedLine = Regex.Replace(trimmedLine, @"\s+", " ");
trimmedLine = trimmedLine.Replace("&&", "\t"); // trimmedLine = trimmedLine.Replace("&&", "\t");
} // }
if (trimmedLine.ToUpper().StartsWith("CREATE TABLE ")) { // if (trimmedLine.ToUpper().StartsWith("CREATE TABLE ")) {
// Start a new table // // Start a new table
table = new SqlTable(); // table = new SqlTable();
sb = new StringBuilder(); // sb = new StringBuilder();
sb.AppendLine(trimmedLine); // sb.AppendLine(trimmedLine);
currentElementType = "table"; // currentElementType = "table";
continue; // continue;
} // }
if (trimmedLine.ToUpper().StartsWith("CREATE INDEX ")) { // if (trimmedLine.ToUpper().StartsWith("CREATE INDEX ")) {
var matches = Regex.Match(trimmedLine, "CREATE INDEX( IF NOT EXISTS)? (\\w*) ON (\\w*)"); // var matches = Regex.Match(trimmedLine, "CREATE INDEX( IF NOT EXISTS)? (\\w*) ON (\\w*)");
if (matches.Success) { // if (matches.Success) {
sb = new StringBuilder(); // sb = new StringBuilder();
sb.AppendLine(trimmedLine); // sb.AppendLine(trimmedLine);
currentElementType = "index"; // currentElementType = "index";
if (!indexes.ContainsKey(matches.Groups[2].Value.Trim())) { // if (!indexes.ContainsKey(matches.Groups[2].Value.Trim())) {
indexes[matches.Groups[2].Value.Trim()] = new List<string>(); // indexes[matches.Groups[2].Value.Trim()] = new List<string>();
} // }
continue; // continue;
} // }
} // }
if (trimmedLine.ToUpper().StartsWith("CREATE TRIGGER ")) { // if (trimmedLine.ToUpper().StartsWith("CREATE TRIGGER ")) {
sb = new StringBuilder(); // sb = new StringBuilder();
sb.AppendLine(trimmedLine); // sb.AppendLine(trimmedLine);
currentElementType = "trigger"; // currentElementType = "trigger";
continue; // continue;
} // }
// The element has concluded (may occur on the same line as above) // // The element has concluded (may occur on the same line as above)
if (trimmedLine.EndsWith(");") || trimmedLine.EndsWith("END;")) { // if (trimmedLine.EndsWith(");") || trimmedLine.EndsWith("END;")) {
sb.AppendLine(trimmedLine); // sb.AppendLine(trimmedLine);
if (currentElementType == "table" && table != null) { // if (currentElementType == "table" && table != null) {
SqlScript += sb.ToString() + Environment.NewLine + Environment.NewLine; // SqlScript += sb.ToString() + Environment.NewLine + Environment.NewLine;
table.ParseSql(sb.ToString()); // table.ParseSql(sb.ToString());
Tables.Add(table); // Tables.Add(table);
currentElementType = ""; // currentElementType = "";
yield return table; // yield return table;
} else if (currentElementType == "index") { // } else if (currentElementType == "index") {
string indexSql = sb.ToString().Replace(Environment.NewLine, " "); // string indexSql = sb.ToString().Replace(Environment.NewLine, " ");
SqlScript += indexSql + Environment.NewLine + Environment.NewLine; // SqlScript += indexSql + Environment.NewLine + Environment.NewLine;
var matches = Regex.Match(indexSql, "CREATE INDEX( IF NOT EXISTS)? (\\w*) ON (\\w*)"); // var matches = Regex.Match(indexSql, "CREATE INDEX( IF NOT EXISTS)? (\\w*) ON (\\w*)");
if (matches.Success) { // if (matches.Success) {
if (!indexes.ContainsKey(matches.Groups[3].Value.Trim())) { // if (!indexes.ContainsKey(matches.Groups[3].Value.Trim())) {
indexes[matches.Groups[3].Value.Trim()] = new List<string>(); // indexes[matches.Groups[3].Value.Trim()] = new List<string>();
} // }
string indexName = matches.Groups[2].Value.Trim(); // string indexName = matches.Groups[2].Value.Trim();
string tableName = matches.Groups[3].Value.Trim(); // string tableName = matches.Groups[3].Value.Trim();
indexes[tableName].Add(indexName + ";" + indexSql); // indexes[tableName].Add(indexName + ";" + indexSql);
} // }
currentElementType = ""; // currentElementType = "";
} else if (currentElementType == "trigger") { // } else if (currentElementType == "trigger") {
string triggerSql = sb.ToString().Replace(Environment.NewLine, " "); // string triggerSql = sb.ToString().Replace(Environment.NewLine, " ");
SqlScript += triggerSql + Environment.NewLine + Environment.NewLine; // SqlScript += triggerSql + Environment.NewLine + Environment.NewLine;
var matches = Regex.Match(triggerSql, "CREATE TRIGGER( IF NOT EXISTS)? (\\w*) ON (\\w*)"); // var matches = Regex.Match(triggerSql, "CREATE TRIGGER( IF NOT EXISTS)? (\\w*) ON (\\w*)");
if (matches.Success) { // if (matches.Success) {
if (!triggers.ContainsKey(matches.Groups[3].Value.Trim())) { // if (!triggers.ContainsKey(matches.Groups[3].Value.Trim())) {
triggers[matches.Groups[3].Value.Trim()] = new List<string>(); // triggers[matches.Groups[3].Value.Trim()] = new List<string>();
} // }
string triggerName = matches.Groups[2].Value.Trim(); // string triggerName = matches.Groups[2].Value.Trim();
string tableName = matches.Groups[3].Value.Trim(); // string tableName = matches.Groups[3].Value.Trim();
triggers[tableName].Add(triggerName + ";" + triggerSql); // triggers[tableName].Add(triggerName + ";" + triggerSql);
} // }
currentElementType = ""; // currentElementType = "";
} // }
continue; // continue;
} // }
sb.AppendLine(trimmedLine); // sb.AppendLine(trimmedLine);
} // }
foreach (string index in indexes.Keys) { // foreach (string index in indexes.Keys) {
table = Tables.FirstOrDefault(t => t.TableName == index); // table = Tables.FirstOrDefault(t => t.TableName == index);
if (table != null) { // if (table != null) {
foreach (string indexSql in indexes[index]) { // foreach (string indexSql in indexes[index]) {
var parts = indexSql.Split(new char[] { ';' }, 2); // var parts = indexSql.Split(new char[] { ';' }, 2);
if (parts.Length == 2) { // if (parts.Length == 2) {
table.Indexes[parts[0]] = parts[1]; // table.Indexes[parts[0]] = parts[1];
} // }
} // }
} // }
} // }
foreach (string trigger in triggers.Keys) { // foreach (string trigger in triggers.Keys) {
table = Tables.FirstOrDefault(t => t.TableName == trigger); // table = Tables.FirstOrDefault(t => t.TableName == trigger);
if (table != null) { // if (table != null) {
foreach (string triggerSql in triggers[trigger]) { // foreach (string triggerSql in triggers[trigger]) {
var parts = triggerSql.Split(new char[] { ';' }, 2); // var parts = triggerSql.Split(new char[] { ';' }, 2);
if (parts.Length == 2) { // if (parts.Length == 2) {
table.Triggers[parts[0]] = parts[1]; // table.Triggers[parts[0]] = parts[1];
} // }
} // }
} // }
} // }
} //}
/// <summary>
/// Generates a SQL script that recreates the database schema, including tables, indexes, and triggers.
/// </summary>
/// <remarks>This method queries the SQLite database schema to retrieve definitions for tables,
/// indexes, and triggers. It then constructs a SQL script that can be used to recreate the schema. The script
/// includes detailed CREATE statements for each table, along with associated indexes and triggers, if any.
/// <para> Tables are processed in alphabetical order, and the "sqlite_sequence" table is excluded from the
/// output. </para></remarks>
/// <param name="dbConnectionString">The connection string used to connect to the SQLite database.</param>
/// <param name="includeIfNotExist">A value indicating whether to include "IF NOT EXISTS" clauses in the generated SQL script. Defaults to <see
/// langword="false"/>.</param>
/// <returns>A <see cref="Task{String}"/> representing the asynchronous operation. The task result contains the generated
/// SQL script as a string.</returns>
public async Task<string> BuildSql(string dbConnectionString, bool includeIfNotExist = false) { public async Task<string> BuildSql(string dbConnectionString, bool includeIfNotExist = false) {
using (SQLiteConnection cn = new SQLiteConnection(dbConnectionString)) { using (SQLiteConnection cn = new SQLiteConnection(dbConnectionString)) {
string sql = ""; string sql = "";
@@ -221,6 +259,7 @@ namespace DbMigrate {
sql += tableSql; sql += tableSql;
} }
SqlScript = sql;
return sql; return sql;
} }
} }

View File

@@ -7,7 +7,7 @@ namespace DbMigrate {
public string TableName { get; set; } public string TableName { get; set; }
public string CreateTableSql { get; set; } public string CreateTableSql { get; set; }
public string OriginalSql { get; set; } public string OriginalSql { get; set; }
public Dictionary<string, string> Columns { get; set; } public ColumnCollection Columns { get; set; }
public Dictionary<string, string> Indexes { get; set; } public Dictionary<string, string> Indexes { get; set; }
public Dictionary<string, string> Triggers { get; set; } public Dictionary<string, string> Triggers { get; set; }
@@ -24,7 +24,7 @@ namespace DbMigrate {
} }
private void initTable() { private void initTable() {
Columns = new Dictionary<string, string>(); Columns = new ColumnCollection();
Indexes = new Dictionary<string, string>(); Indexes = new Dictionary<string, string>();
Triggers = new Dictionary<string, string>(); Triggers = new Dictionary<string, string>();
CreateTableSql = ""; CreateTableSql = "";
@@ -54,15 +54,11 @@ namespace DbMigrate {
} }
public string[] GetColumnNames() { public string[] GetColumnNames() {
return Columns.Keys.ToArray(); return Columns.GetColumnNames();
}
public string[] GetColumns() {
return Columns.Values.ToArray();
} }
public bool HasColumn(string columnName) { public bool HasColumn(string columnName) {
return Columns.ContainsKey(columnName); return Columns.Contains(columnName);
} }
public string[] GetTriggerNames() { public string[] GetTriggerNames() {