diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c03250..81d336f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Represents the **NuGet** versions. +## v2.3.9 +- *Fixed:* The YAML-based `MigrationCommand.Data` logic previously set the `CreatedBy`, `CreatedDate`, `UpdatedBy` and `UpdatedDate` (or specified equivalent) regardless of whether the data was being inserted or merged (insert/update). This has been corrected such that the appropriate values are only set for the specific type of operation being performed; i.e. `Created*`-only or `Updated*`-only. +- *Fixed:* Enabled additional command-line arguments to be passed for `MigrationCommand.CodeGen` to enable inherited usage flexibility. Removed `MigrationArgsBase.ScriptName` and `MigrationArgsBase.ScriptArguments` and included within existing `MigrationArgsBase.Parameters` as singular dictionary of key/value pairs (simplification). + ## v2.3.8 - *Fixed:* `SqlServerMigration` has been fixed to handle column size of `MAX` correctly. diff --git a/Common.targets b/Common.targets index ead0a5c..18425e9 100644 --- a/Common.targets +++ b/Common.targets @@ -1,6 +1,6 @@ - 2.3.8 + 2.3.9 preview Avanade Avanade diff --git a/src/DbEx.MySql/MySqlSchemaConfig.cs b/src/DbEx.MySql/MySqlSchemaConfig.cs index 6b414c0..dc5b3c7 100644 --- a/src/DbEx.MySql/MySqlSchemaConfig.cs +++ b/src/DbEx.MySql/MySqlSchemaConfig.cs @@ -67,6 +67,11 @@ public override void PrepareDataParserArgs(DataParserArgs dataParserArgs) dataParserArgs.RefDataColumnDefaults.TryAdd("is_active", _ => true); dataParserArgs.RefDataColumnDefaults.TryAdd("sort_order", i => i); } + + dataParserArgs.CreatedByColumnName ??= CreatedByColumnName; + dataParserArgs.CreatedDateColumnName ??= CreatedDateColumnName; + dataParserArgs.UpdatedByColumnName ??= UpdatedByColumnName; + dataParserArgs.UpdatedDateColumnName ??= UpdatedDateColumnName; } /// diff --git a/src/DbEx.MySql/Resources/DatabaseData_sql.hbs b/src/DbEx.MySql/Resources/DatabaseData_sql.hbs index 15cf8da..91b746d 100644 --- a/src/DbEx.MySql/Resources/DatabaseData_sql.hbs +++ b/src/DbEx.MySql/Resources/DatabaseData_sql.hbs @@ -1,12 +1,12 @@ {{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/DbEx }} {{#if IsMerge}} {{#each Rows}} -INSERT INTO `{{Table.Name}}` ({{#each Columns}}`{{Name}}`{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each Columns}}{{#if UseForeignKeyQueryForId}}(SELECT `{{DbColumn.ForeignColumn}}` FROM `{{DbColumn.ForeignTable}}` WHERE `{{DbColumn.ForeignRefDataCodeColumn}}` = {{{SqlValue}}} LIMIT 1){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}) ON DUPLICATE KEY UPDATE {{#each Table.MergeUpdateColumns}}`{{Name}}` = VALUES(`{{Name}}`){{#unless @last}}, {{/unless}}{{/each}}; +INSERT INTO `{{Table.Name}}` ({{#each MergeInsertColumns}}`{{Name}}`{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each MergeInsertColumns}}{{#if UseForeignKeyQueryForId}}(SELECT `{{DbColumn.ForeignColumn}}` FROM `{{DbColumn.ForeignTable}}` WHERE `{{DbColumn.ForeignRefDataCodeColumn}}` = {{{SqlValue}}} LIMIT 1){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}) ON DUPLICATE KEY UPDATE {{#each MergeUpdateColumns}}`{{Name}}` = {{{SqlValue}}}{{#unless @last}}, {{/unless}}{{/each}}; {{/each}} SELECT {{Rows.Count}}; -- Total rows upserted {{else}} {{#each Rows}} -INSERT INTO `{{Table.Name}}` ({{#each Columns}}`{{Name}}`{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each Columns}}{{#if UseForeignKeyQueryForId}}(SELECT `{{DbColumn.ForeignColumn}}` FROM `{{DbColumn.ForeignTable}}` WHERE `{{DbColumn.ForeignRefDataCodeColumn}}` = {{{SqlValue}}} LIMIT 1){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}); +INSERT INTO `{{Table.Name}}` ({{#each InsertColumns}}`{{Name}}`{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each InsertColumns}}{{#if UseForeignKeyQueryForId}}(SELECT `{{DbColumn.ForeignColumn}}` FROM `{{DbColumn.ForeignTable}}` WHERE `{{DbColumn.ForeignRefDataCodeColumn}}` = {{{SqlValue}}} LIMIT 1){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}); {{/each}} SELECT {{Rows.Count}}; -- Total rows inserted {{/if}} \ No newline at end of file diff --git a/src/DbEx.SqlServer/Resources/DatabaseData_sql.hbs b/src/DbEx.SqlServer/Resources/DatabaseData_sql.hbs index ed84506..63020cf 100644 --- a/src/DbEx.SqlServer/Resources/DatabaseData_sql.hbs +++ b/src/DbEx.SqlServer/Resources/DatabaseData_sql.hbs @@ -18,14 +18,14 @@ MERGE INTO [{{Schema}}].[{{Name}}] WITH (HOLDLOCK) as [t] SELECT {{#each MergeMatchColumns}}[t].[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}) THEN UPDATE SET {{#each MergeUpdateColumns}}[t].[{{Name}}] = [s].[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}} WHEN NOT MATCHED BY TARGET - THEN INSERT ({{#each Columns}}[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}) - VALUES ({{#each Columns}}[s].[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}); + THEN INSERT ({{#each MergeInsertColumns}}[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}) + VALUES ({{#each MergeInsertColumns}}[s].[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}); SELECT @@ROWCOUNT DROP TABLE #temp {{else}} {{#each Rows}} -INSERT INTO [{{Table.Schema}}].[{{Table.Name}}] ({{#each Columns}}[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each Columns}}{{#if UseForeignKeyQueryForId}}(SELECT TOP 1 [{{DbColumn.ForeignColumn}}] FROM [{{DbColumn.ForeignSchema}}].[{{DbColumn.ForeignTable}}] WHERE [{{DbColumn.ForeignRefDataCodeColumn}}] = {{{SqlValue}}}){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}) +INSERT INTO [{{Table.Schema}}].[{{Table.Name}}] ({{#each InsertColumns}}[{{Name}}]{{#unless @last}}, {{/unless}}{{/each}}) VALUES ({{#each InsertColumns}}{{#if UseForeignKeyQueryForId}}(SELECT TOP 1 [{{DbColumn.ForeignColumn}}] FROM [{{DbColumn.ForeignSchema}}].[{{DbColumn.ForeignTable}}] WHERE [{{DbColumn.ForeignRefDataCodeColumn}}] = {{{SqlValue}}}){{else}}{{{SqlValue}}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}) {{/each}} SELECT {{Rows.Count}} -- Total rows inserted {{/if}} \ No newline at end of file diff --git a/src/DbEx.SqlServer/SqlServerSchemaConfig.cs b/src/DbEx.SqlServer/SqlServerSchemaConfig.cs index b67edda..8198cce 100644 --- a/src/DbEx.SqlServer/SqlServerSchemaConfig.cs +++ b/src/DbEx.SqlServer/SqlServerSchemaConfig.cs @@ -67,6 +67,11 @@ public override void PrepareDataParserArgs(DataParserArgs dataParserArgs) dataParserArgs.RefDataColumnDefaults.TryAdd("IsActive", _ => true); dataParserArgs.RefDataColumnDefaults.TryAdd("SortOrder", i => i); } + + dataParserArgs.CreatedByColumnName ??= CreatedByColumnName; + dataParserArgs.CreatedDateColumnName ??= CreatedDateColumnName; + dataParserArgs.UpdatedByColumnName ??= UpdatedByColumnName; + dataParserArgs.UpdatedDateColumnName ??= UpdatedDateColumnName; } /// diff --git a/src/DbEx/Console/MigrationConsoleBase.cs b/src/DbEx/Console/MigrationConsoleBase.cs index 83eab1f..3684f24 100644 --- a/src/DbEx/Console/MigrationConsoleBase.cs +++ b/src/DbEx/Console/MigrationConsoleBase.cs @@ -21,8 +21,8 @@ namespace DbEx.Console /// /// Base console that facilitates the by managing the standard console command-line arguments/options. /// - /// The standard console command-line arguments/options can be controlled via the constructor using the flags. Additional capabilities can be added by inherting and overridding the - /// , and . Changes to the console output can be achieved by overridding + /// The standard console command-line arguments/options can be controlled via the constructor using the flags. Additional capabilities can be added by inheriting and overriding the + /// , and . Changes to the console output can be achieved by overriding /// , , and . /// The underlying command line parsing is provided by . public abstract class MigrationConsoleBase @@ -160,20 +160,14 @@ public async Task RunAsync(string[] args, CancellationToken cancellationTok if (vr != ValidationResult.Success) return vr; - if (_additionalArgs.Values.Count > 0 && !(Args.MigrationCommand.HasFlag(MigrationCommand.Script) || Args.MigrationCommand.HasFlag(MigrationCommand.Execute))) - return new ValidationResult($"Additional arguments can only be specified when the command is '{nameof(MigrationCommand.Script)}' or '{nameof(MigrationCommand.Execute)}'.", new string[] { "args" }); + if (_additionalArgs.Values.Count > 0 && !(Args.MigrationCommand.HasFlag(MigrationCommand.CodeGen) || Args.MigrationCommand.HasFlag(MigrationCommand.Script) || Args.MigrationCommand.HasFlag(MigrationCommand.Execute))) + return new ValidationResult($"Additional arguments can only be specified when the command is '{nameof(MigrationCommand.CodeGen)}', '{nameof(MigrationCommand.Script)}' or '{nameof(MigrationCommand.Execute)}'.", new string[] { "args" }); - if (Args.MigrationCommand.HasFlag(MigrationCommand.Script)) + if (Args.MigrationCommand.HasFlag(MigrationCommand.CodeGen) || Args.MigrationCommand.HasFlag(MigrationCommand.Script)) { for (int i = 0; i < _additionalArgs.Values.Count; i++) { - if (i == 0) - Args.ScriptName = _additionalArgs.Values[i]; - else - { - Args.ScriptArguments ??= new Dictionary(); - Args.ScriptArguments.Add($"Param{i}", _additionalArgs.Values[i]); - } + Args.Parameters.Add($"Param{i}", _additionalArgs.Values[i]); } } diff --git a/src/DbEx/DatabaseExtensions.cs b/src/DbEx/DatabaseExtensions.cs index 765fcea..446ad76 100644 --- a/src/DbEx/DatabaseExtensions.cs +++ b/src/DbEx/DatabaseExtensions.cs @@ -38,7 +38,11 @@ public static async Task> SelectSchemaAsync(this IDatabase d // Get all the tables and their columns. using var sr = DatabaseMigrationBase.GetRequiredResourcesStreamReader("SelectTableAndColumns.sql", new Assembly[] { typeof(DatabaseExtensions).Assembly }); +#if NET7_0_OR_GREATER + await database.SqlStatement(await sr.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).SelectQueryAsync(dr => +#else await database.SqlStatement(await sr.ReadToEndAsync().ConfigureAwait(false)).SelectQueryAsync(dr => +#endif { if (!databaseSchemaConfig.SupportsSchema && dr.GetValue("TABLE_SCHEMA") != databaseSchemaConfig.DatabaseName) return 0; @@ -52,6 +56,9 @@ await database.SqlStatement(await sr.ReadToEndAsync().ConfigureAwait(false)).Sel tables.Add(table = dt); var dc = databaseSchemaConfig.CreateColumnFromInformationSchema(table, dr); + dc.IsCreatedAudit = dc.Name == (dataParserArgs?.CreatedByColumnName ?? databaseSchemaConfig.CreatedByColumnName) || dc.Name == (dataParserArgs?.CreatedDateColumnName ?? databaseSchemaConfig.CreatedDateColumnName); + dc.IsUpdatedAudit = dc.Name == (dataParserArgs?.UpdatedByColumnName ?? databaseSchemaConfig.UpdatedByColumnName) || dc.Name == (dataParserArgs?.UpdatedDateColumnName ?? databaseSchemaConfig.UpdatedDateColumnName); + table.Columns.Add(dc); return 0; }, cancellationToken).ConfigureAwait(false); @@ -70,7 +77,11 @@ await database.SqlStatement(await sr.ReadToEndAsync().ConfigureAwait(false)).Sel // Configure all the single column primary and unique constraints. using var sr2 = DatabaseMigrationBase.GetRequiredResourcesStreamReader("SelectTablePrimaryKey.sql", new Assembly[] { typeof(DatabaseExtensions).Assembly }); +#if NET7_0_OR_GREATER + var pks = await database.SqlStatement(await sr2.ReadToEndAsync(cancellationToken).ConfigureAwait(false)).SelectQueryAsync(dr => new +#else var pks = await database.SqlStatement(await sr2.ReadToEndAsync().ConfigureAwait(false)).SelectQueryAsync(dr => new +#endif { ConstraintName = dr.GetValue("CONSTRAINT_NAME"), TableSchema = dr.GetValue("TABLE_SCHEMA"), diff --git a/src/DbEx/DatabaseSchemaConfig.cs b/src/DbEx/DatabaseSchemaConfig.cs index 4efb695..24fe499 100644 --- a/src/DbEx/DatabaseSchemaConfig.cs +++ b/src/DbEx/DatabaseSchemaConfig.cs @@ -106,7 +106,7 @@ protected DatabaseSchemaConfig(string databaseName, bool supportsSchema = true) /// Opportunity to load additional `InformationSchema` related data that is specific to the database. /// /// The . - /// The list to load addtional data into. + /// The list to load additional data into. /// The . /// The . public virtual Task LoadAdditionalInformationSchema(IDatabase database, List tables, DataParserArgs? dataParserArgs, CancellationToken cancellationToken) => Task.CompletedTask; @@ -143,21 +143,21 @@ protected DatabaseSchemaConfig(string databaseName, bool supportsSchema = true) public abstract string ToFormattedSqlStatementValue(DataParserArgs dataParserArgs, object? value); /// - /// Inidicates whether the is considered an integer. + /// Indicates whether the is considered an integer. /// /// The database type. /// true indicates it is; otherwise, false. public abstract bool IsDbTypeInteger(string? dbType); /// - /// Inidicates whether the is considered a decimal. + /// Indicates whether the is considered a decimal. /// /// The database type. /// true indicates it is; otherwise, false. public abstract bool IsDbTypeDecimal(string? dbType); /// - /// Inidicates whether the is considered a string. + /// Indicates whether the is considered a string. /// /// The database type. /// true indicates it is; otherwise, false. diff --git a/src/DbEx/DbSchema/DbColumnSchema.cs b/src/DbEx/DbSchema/DbColumnSchema.cs index ca2738a..9af86b7 100644 --- a/src/DbEx/DbSchema/DbColumnSchema.cs +++ b/src/DbEx/DbSchema/DbColumnSchema.cs @@ -12,6 +12,7 @@ namespace DbEx.DbSchema public class DbColumnSchema { private string? _dotNetType; + private string? _dotNetName; private string? _sqlType; /// @@ -127,22 +128,38 @@ public DbColumnSchema(DbTableSchema dbTable, string name, string type) /// public string? ForeignRefDataCodeColumn { get; set; } + /// + /// Indicates whether the column is a created audit column; i.e. name is CreatedDate or CreatedBy. + /// + public bool IsCreatedAudit { get; set; } + + /// + /// Indicates whether the column is an updated audit column; i.e. name is UpdatedDate or UpdatedBy. + /// + public bool IsUpdatedAudit { get; set; } + /// /// Gets the corresponding .NET name. /// - public string DotNetType => _dotNetType ?? DbTable?.Config.ToDotNetTypeName(this) ?? throw new InvalidOperationException($"The {nameof(DbTable)} must be set before the {nameof(DotNetType)} property can be accessed."); + public string DotNetType => _dotNetType ??= DbTable?.Config.ToDotNetTypeName(this) ?? throw new InvalidOperationException($"The {nameof(DbTable)} must be set before the {nameof(DotNetType)} property can be accessed."); + + /// + /// Gets the corresponding .NET name. + /// + public string DotNetName => _dotNetName ??= DbTableSchema.CreateDotNetName(Name); /// /// Gets the fully defined SQL type. /// - public string SqlType => _sqlType ?? DbTable?.Config.ToFormattedSqlType(this) ?? throw new InvalidOperationException($"The {nameof(DbTable)} must be set before the {nameof(SqlType)} property can be accessed."); + public string SqlType => _sqlType ??= DbTable?.Config.ToFormattedSqlType(this) ?? throw new InvalidOperationException($"The {nameof(DbTable)} must be set before the {nameof(SqlType)} property can be accessed."); /// - /// Prepares the schema by updating the calcuated properties: and . + /// Prepares the schema by updating the calculated properties: , and . /// public void Prepare() { _dotNetType = DbTable.Config.ToDotNetTypeName(this); + _dotNetName = DbTableSchema.CreateDotNetName(Name); _sqlType = DbTable.Config.ToFormattedSqlType(this); } @@ -179,7 +196,10 @@ public void CopyFrom(DbColumnSchema column) ForeignColumn = column.ForeignColumn; IsForeignRefData = column.IsForeignRefData; ForeignRefDataCodeColumn = column.ForeignRefDataCodeColumn; + IsCreatedAudit = column.IsCreatedAudit; + IsUpdatedAudit = column.IsUpdatedAudit; _dotNetType = column._dotNetType; + _dotNetName = column._dotNetName; _sqlType = column._sqlType; } } diff --git a/src/DbEx/DbSchema/DbTableSchema.cs b/src/DbEx/DbSchema/DbTableSchema.cs index 1a670bf..aaba8ca 100644 --- a/src/DbEx/DbSchema/DbTableSchema.cs +++ b/src/DbEx/DbSchema/DbTableSchema.cs @@ -7,6 +7,7 @@ using System.Data; using System.Diagnostics; using System.Linq; +using System.Text; using System.Text.RegularExpressions; namespace DbEx.DbSchema @@ -17,6 +18,9 @@ namespace DbEx.DbSchema [DebuggerDisplay("{QualifiedName}")] public class DbTableSchema { + private string? _dotNetName; + private string? _pluralName; + /// /// The expression pattern for splitting strings into words. /// @@ -37,6 +41,36 @@ public static string CreateAlias(string name) return new string(s.Replace(" ", " ").Replace("_", " ").Replace("-", " ").Split(' ').Where(x => !string.IsNullOrEmpty(x)).Select(x => x[..1].ToLower(System.Globalization.CultureInfo.InvariantCulture).ToCharArray()[0]).ToArray()); } + /// + /// Create a .NET friendly name. + /// + /// The name. + /// The .NET friendly name. + public static string CreateDotNetName(string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException(nameof(name)); + + var sb = new StringBuilder(); + name.Split(new char[] { '_', '-' }, StringSplitOptions.RemoveEmptyEntries).ForEach(part => sb.Append(StringConverter.ToPascalCase(part))); + return sb.ToString(); + } + + /// + /// Create a plural from the name. + /// + /// The name. + /// The pluralized name. + public static string CreatePluralName(string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException(nameof(name)); + + var words = Regex.Split(name, WordSplitPattern).Where(x => !string.IsNullOrEmpty(x)).ToList(); + words[^1] = StringConverter.ToPlural(words[^1]); + return string.Join(string.Empty, words); + } + /// /// Initializes a new instance of the class. /// @@ -62,6 +96,16 @@ public DbTableSchema(DatabaseSchemaConfig config, string schema, string name) /// public string Name { get; } + /// + /// Gets the table name in .NET friendly form. + /// + public string DotNetName => _dotNetName ??= CreateDotNetName(Name); + + /// + /// Gets the in plural form. + /// + public string PluralName => _pluralName ??= CreatePluralName(DotNetName); + /// /// Gets the schema name. /// diff --git a/src/DbEx/Migration/Data/DataRow.cs b/src/DbEx/Migration/Data/DataRow.cs index 8d86168..ca35292 100644 --- a/src/DbEx/Migration/Data/DataRow.cs +++ b/src/DbEx/Migration/Data/DataRow.cs @@ -27,6 +27,21 @@ public class DataRow /// public List Columns { get; } = new List(); + /// + /// Gets the insert columns. + /// + public List InsertColumns => Columns.Where(c => Table.InsertColumns.Any(x => x.Name == c.Name)).ToList(); + + /// + /// Gets the columns that are used for the merge insert. + /// + public List MergeInsertColumns => Columns.Where(c => Table.MergeInsertColumns.Any(x => x.Name == c.Name)).ToList(); + + /// + /// Gets the columns that are used for the merge update. + /// + public List MergeUpdateColumns => Columns.Where(c => Table.MergeUpdateColumns.Any(x => x.Name == c.Name)).ToList(); + /// /// Adds a to the row using the specified name and value. /// diff --git a/src/DbEx/Migration/Data/DataTable.cs b/src/DbEx/Migration/Data/DataTable.cs index 145dbad..1d6b48f 100644 --- a/src/DbEx/Migration/Data/DataTable.cs +++ b/src/DbEx/Migration/Data/DataTable.cs @@ -109,17 +109,25 @@ internal DataTable(DataParser parser, string schema, string name) /// public List Columns { get; } = new List(); + /// + /// Gets the insert columns. + /// + public List InsertColumns => Columns.Where(x => !x.IsUpdatedAudit).ToList(); + /// /// Gets the merge match columns. /// - public List MergeMatchColumns => Columns.Where(x => - !(x.Name == Args.CreatedDateColumnName || x.Name == Args.CreatedByColumnName || x.Name == Args.UpdatedDateColumnName || x.Name == Args.UpdatedDateColumnName) - && !(UseIdentifierGenerator && x.IsPrimaryKey)).ToList(); + public List MergeMatchColumns => Columns.Where(x => !x.IsCreatedAudit && !x.IsUpdatedAudit && !(UseIdentifierGenerator && x.IsPrimaryKey)).ToList(); + + /// + /// Gets the merge insert columns. + /// + public List MergeInsertColumns => Columns.Where(x => !x.IsUpdatedAudit).ToList(); /// /// Gets the merge update columns. /// - public List MergeUpdateColumns => Columns.Where(x => !(UseIdentifierGenerator && x.IsPrimaryKey)).ToList(); + public List MergeUpdateColumns => Columns.Where(x => !x.IsCreatedAudit).ToList(); /// /// Gets the primary key columns. diff --git a/src/DbEx/Migration/DatabaseMigrationBase.cs b/src/DbEx/Migration/DatabaseMigrationBase.cs index 3b7f041..35543e8 100644 --- a/src/DbEx/Migration/DatabaseMigrationBase.cs +++ b/src/DbEx/Migration/DatabaseMigrationBase.cs @@ -169,7 +169,7 @@ public virtual async Task MigrateAsync(CancellationToken cancellationToken // Where only creating a new script, then quickly do it and get out of here! if (Args.MigrationCommand.HasFlag(MigrationCommand.Script)) - return await CreateScriptAsync(Args.ScriptName, Args.ScriptArguments, cancellationToken).ConfigureAwait(false); + return await CreateScriptAsync(Args.Parameters["Param0"]!.ToString(), Args.CreateStringParameters(), cancellationToken).ConfigureAwait(false); // Where only executing SQL statement, then execute and get out of here! if (Args.MigrationCommand.HasFlag(MigrationCommand.Execute)) @@ -353,7 +353,7 @@ protected virtual async Task ExecuteScriptsAsync(IEnumerable /// Gets the runtime parameters. /// - /// The following parameter names are reserved for a specific internal purpose: , and . + /// The following parameter names are reserved for a specific internal purpose: , and . + /// and can support additional command-line arguments; these are automatically added as 'ParamN' where 'N' is the zero-based index; e.g. 'Param0'. public Dictionary Parameters { get; } = new Dictionary(); /// @@ -76,16 +77,6 @@ public abstract class MigrationArgsBase : OnRamp.CodeGeneratorDbArgsBase /// public DataParserArgs DataParserArgs { get; set; } - /// - /// Gets or sets the name. - /// - public string? ScriptName { get; set; } - - /// - /// Gets or sets the arguments. - /// - public IDictionary? ScriptArguments { get; set; } - /// /// Gets or sets the statements. /// @@ -173,17 +164,8 @@ protected void CopyFrom(MigrationArgsBase args) SchemaOrder.Clear(); SchemaOrder.AddRange(args.SchemaOrder); DataParserArgs.CopyFrom(args.DataParserArgs); - ScriptName = args.ScriptName; DataResetFilterPredicate = args.DataResetFilterPredicate; - if (args.ScriptArguments == null) - ScriptArguments = null; - else - { - ScriptArguments = new Dictionary(); - args.ScriptArguments.ForEach(x => ScriptArguments.Add(x.Key, x.Value)); - } - if (args.ExecuteStatements == null) ExecuteStatements = null; else @@ -192,5 +174,22 @@ protected void CopyFrom(MigrationArgsBase args) ExecuteStatements.AddRange(args.ExecuteStatements); } } + + /// + /// Creates a copy of the where all are converted to a or null. + /// + public IDictionary CreateStringParameters() + { + var dict = new Dictionary(); + foreach (var item in Parameters) + { + if (item.Value == null) + dict.Add(item.Key, null); + else + dict.Add(item.Key, item.Value.ToString()); + } + + return dict; + } } } \ No newline at end of file diff --git a/tests/DbEx.Test.Console/Migrations/003-create-test-gender-table.sql b/tests/DbEx.Test.Console/Migrations/003-create-test-gender-table.sql index 342bf8d..ff64743 100644 --- a/tests/DbEx.Test.Console/Migrations/003-create-test-gender-table.sql +++ b/tests/DbEx.Test.Console/Migrations/003-create-test-gender-table.sql @@ -1,5 +1,9 @@  CREATE TABLE [Test].[Gender] ( [GenderId] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, [Code] NVARCHAR (50) NOT NULL UNIQUE, - [Text] VARCHAR (256) NOT NULL + [Text] VARCHAR (256) NOT NULL, + [CreatedBy] NVARCHAR (50) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR (50) NULL, + [UpdatedDate] DATETIME2 NULL ) \ No newline at end of file diff --git a/tests/DbEx.Test.Console/Migrations/006-create-test-person-table.sql b/tests/DbEx.Test.Console/Migrations/006-create-test-person-table.sql index 30f7f19..ecae200 100644 --- a/tests/DbEx.Test.Console/Migrations/006-create-test-person-table.sql +++ b/tests/DbEx.Test.Console/Migrations/006-create-test-person-table.sql @@ -1,6 +1,8 @@  CREATE TABLE [Test].[Person] ( [PersonId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, [Name] NVARCHAR (200) NOT NULL, - [CreatedBy] NVARCHAR (200) NOT NULL, - [CreatedDate] DATETIME2 NOT NULL + [CreatedBy] NVARCHAR (200) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR (200) NULL, + [UpdatedDate] DATETIME2 NULL ) \ No newline at end of file diff --git a/tests/DbEx.Test.MySqlConsole/Migrations/003-create-test-gender-table.sql b/tests/DbEx.Test.MySqlConsole/Migrations/003-create-test-gender-table.sql index 2d61a36..5752605 100644 --- a/tests/DbEx.Test.MySqlConsole/Migrations/003-create-test-gender-table.sql +++ b/tests/DbEx.Test.MySqlConsole/Migrations/003-create-test-gender-table.sql @@ -1,5 +1,9 @@  CREATE TABLE `gender` ( `gender_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR (50) NOT NULL UNIQUE, - `text` VARCHAR (256) NOT NULL + `text` VARCHAR (256) NOT NULL, + `created_by` VARCHAR (50) NULL, + `created_date` DATETIME NULL, + `updated_by` VARCHAR (50) NULL, + `updated_date` DATETIME NULL ) \ No newline at end of file diff --git a/tests/DbEx.Test.MySqlConsole/Migrations/004-create-test-contact-table.sql b/tests/DbEx.Test.MySqlConsole/Migrations/004-create-test-contact-table.sql index 932cb2b..75a6e4c 100644 --- a/tests/DbEx.Test.MySqlConsole/Migrations/004-create-test-contact-table.sql +++ b/tests/DbEx.Test.MySqlConsole/Migrations/004-create-test-contact-table.sql @@ -6,5 +6,9 @@ `contact_type_id` INT NOT NULL DEFAULT 1, `gender_id` INT NULL, `notes` TEXT NULL, + `created_by` VARCHAR (50) NULL, + `created_date` DATETIME NULL, + `updated_by` VARCHAR (50) NULL, + `updated_date` DATETIME NULL, CONSTRAINT `FK_Test_Contact_ContactType` FOREIGN KEY (`contact_type_id`) REFERENCES `contact_type` (`contact_type_id`) ) \ No newline at end of file diff --git a/tests/DbEx.Test/DatabaseSchemaTest.cs b/tests/DbEx.Test/DatabaseSchemaTest.cs index d4e450e..6af7510 100644 --- a/tests/DbEx.Test/DatabaseSchemaTest.cs +++ b/tests/DbEx.Test/DatabaseSchemaTest.cs @@ -43,6 +43,8 @@ public async Task SqlServerSelectSchema() Assert.IsTrue(tab.IsRefData); Assert.AreEqual(4, tab.Columns.Count); Assert.AreEqual(1, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("ContactType", tab.DotNetName); + Assert.AreEqual("ContactTypes", tab.PluralName); var col = tab.Columns[0]; Assert.AreEqual("ContactTypeId", col.Name); @@ -103,6 +105,8 @@ public async Task SqlServerSelectSchema() Assert.IsFalse(tab.IsRefData); Assert.AreEqual(8, tab.Columns.Count); Assert.AreEqual(1, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("Contact", tab.DotNetName); + Assert.AreEqual("Contacts", tab.PluralName); col = tab.Columns[0]; Assert.AreEqual("ContactId", col.Name); @@ -221,6 +225,8 @@ public async Task SqlServerSelectSchema() Assert.IsFalse(tab.IsRefData); Assert.AreEqual(4, tab.Columns.Count); Assert.AreEqual(2, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("MultiPk", tab.DotNetName); + Assert.AreEqual("MultiPks", tab.PluralName); col = tab.Columns[0]; Assert.AreEqual("Part1", col.Name); @@ -315,8 +321,10 @@ public async Task SqlServerSelectSchema() Assert.AreEqual("[Test].[Person]", tab.QualifiedName); Assert.IsFalse(tab.IsAView); Assert.IsFalse(tab.IsRefData); - Assert.AreEqual(4, tab.Columns.Count); + Assert.AreEqual(6, tab.Columns.Count); Assert.AreEqual(1, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("Person", tab.DotNetName); + Assert.AreEqual("People", tab.PluralName); col = tab.Columns[0]; Assert.AreEqual("PersonId", col.Name); @@ -365,6 +373,8 @@ public async Task MySqlSelectSchema() Assert.IsTrue(tab.IsRefData); Assert.AreEqual(4, tab.Columns.Count); Assert.AreEqual(1, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("ContactType", tab.DotNetName); + Assert.AreEqual("ContactTypes", tab.PluralName); var col = tab.Columns[0]; Assert.AreEqual("contact_type_id", col.Name); @@ -386,6 +396,7 @@ public async Task MySqlSelectSchema() Assert.IsNull(col.ForeignTable); Assert.IsNull(col.ForeignColumn); Assert.IsNull(col.DefaultValue); + Assert.AreEqual("ContactTypeId", col.DotNetName); col = tab.Columns[1]; Assert.AreEqual("code", col.Name); @@ -407,6 +418,7 @@ public async Task MySqlSelectSchema() Assert.IsNull(col.ForeignTable); Assert.IsNull(col.ForeignColumn); Assert.IsNull(col.DefaultValue); + Assert.AreEqual("Code", col.DotNetName); col = tab.Columns[2]; Assert.AreEqual("text", col.Name); @@ -423,8 +435,11 @@ public async Task MySqlSelectSchema() Assert.AreEqual("`contact`", tab.QualifiedName); Assert.IsFalse(tab.IsAView); Assert.IsFalse(tab.IsRefData); - Assert.AreEqual(7, tab.Columns.Count); + Assert.AreEqual(11, tab.Columns.Count); Assert.AreEqual(1, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("Contact", tab.DotNetName); + Assert.AreEqual("Contacts", tab.PluralName); + col = tab.Columns[0]; Assert.AreEqual("contact_id", col.Name); @@ -446,6 +461,7 @@ public async Task MySqlSelectSchema() Assert.IsNull(col.ForeignTable); Assert.IsNull(col.ForeignColumn); Assert.IsNull(col.DefaultValue); + Assert.AreEqual("ContactId", col.DotNetName); col = tab.Columns[3]; Assert.AreEqual("date_of_birth", col.Name); @@ -467,6 +483,7 @@ public async Task MySqlSelectSchema() Assert.IsNull(col.ForeignTable); Assert.IsNull(col.ForeignColumn); Assert.IsNull(col.DefaultValue); + Assert.AreEqual("DateOfBirth", col.DotNetName); col = tab.Columns[4]; Assert.AreEqual("contact_type_id", col.Name); @@ -489,6 +506,7 @@ public async Task MySqlSelectSchema() Assert.AreEqual("contact_type_id", col.ForeignColumn); Assert.AreEqual("code", col.ForeignRefDataCodeColumn); Assert.AreEqual("1", col.DefaultValue); + Assert.AreEqual("ContactTypeId", col.DotNetName); col = tab.Columns[5]; Assert.AreEqual("gender_id", col.Name); @@ -540,6 +558,8 @@ public async Task MySqlSelectSchema() Assert.IsFalse(tab.IsRefData); Assert.AreEqual(4, tab.Columns.Count); Assert.AreEqual(2, tab.PrimaryKeyColumns.Count); + Assert.AreEqual("MultiPk", tab.DotNetName); + Assert.AreEqual("MultiPks", tab.PluralName); col = tab.Columns[0]; Assert.AreEqual("part1", col.Name);