Skip to content

Commit 2fbc221

Browse files
committed
Refactor and enhance to now avoid Schema Locks as default behavior in the ExecuteMaterializeDataProcessAsync(); but is configurable. The the new MaterializedDataSchemaCopyMode config value controls if Schema copying/cloning (for Loading/Discarding Tables) is inside or outside the Transaction (e.g. SchemaCopyMode.InsideTransactionAllowSchemaLocks vs OutsideTransactionAvoidSchemaLocks). New separate methods is now added to handle the CleanupMaterializeDataProcessAsync() but must be explicitly called as it is no longer implicitly called with FinishMaterializeDataProcessAsync(). Fix bug in ReSeedTableIdentityValueWithMaxIdAsync() when the Table is Empty so that it now defaults to value of 1. Added support to customize the Prefix & Suffix of cloned tables in the Materialized Data process for the Loading table name & Discarding table name (e.g. MaterializedDataLoadingTablePrefix, MaterializedDataDiscardingTablePrefix, etc.).
All Unit Tests are passing, and Nuget Package has been updated now as v2.3.0.
1 parent 2c868c5 commit 2fbc221

File tree

12 files changed

+339
-142
lines changed

12 files changed

+339
-142
lines changed

NetStandard.SqlBulkHelpers/Database/TableNameTerm.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@ public TableNameTerm(string schemaName, string tableName)
2323
public string FullyQualifiedTableName { get; }
2424

2525
public override string ToString() => FullyQualifiedTableName;
26-
public bool Equals(TableNameTerm other) => FullyQualifiedTableName.Equals(other.FullyQualifiedTableName);
27-
public bool EqualsIgnoreCase(TableNameTerm other) => FullyQualifiedTableName.Equals(other.FullyQualifiedTableName, StringComparison.OrdinalIgnoreCase);
28-
public TableNameTerm SwitchSchema(string newSchema) => new TableNameTerm(newSchema, TableName);
29-
26+
27+
public bool Equals(TableNameTerm other)
28+
=> FullyQualifiedTableName.Equals(other.FullyQualifiedTableName);
29+
30+
public bool EqualsIgnoreCase(TableNameTerm other)
31+
=> FullyQualifiedTableName.Equals(other.FullyQualifiedTableName, StringComparison.OrdinalIgnoreCase);
32+
33+
public TableNameTerm SwitchSchema(string newSchema)
34+
=> new TableNameTerm(newSchema, TableName);
35+
36+
public TableNameTerm ApplyNamePrefixOrSuffix(string prefix = null, string suffix = null)
37+
=> new TableNameTerm(SchemaName, string.Concat(prefix?.Trim() ?? string.Empty, TableName, suffix?.Trim() ?? string.Empty));
38+
3039
//Handle Automatic String conversions for simplified APIs...
3140
public static implicit operator string(TableNameTerm t) => t.ToString();
3241

NetStandard.SqlBulkHelpers/MaterializedData/CloneTableInfo.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public CloneTableInfo MakeTargetTableNameUnique()
3030
=> new CloneTableInfo(SourceTable, MakeTableNameUniqueInternal(TargetTable));
3131

3232
private static TableNameTerm MakeTableNameUniqueInternal(TableNameTerm tableNameTerm)
33-
=> TableNameTerm.From(tableNameTerm.SchemaName, $"{tableNameTerm.TableName}_Copy_{IdGenerator.NewId(10)}");
33+
=> TableNameTerm.From(tableNameTerm.SchemaName, string.Concat(tableNameTerm.TableName, "_", IdGenerator.NewId(10)));
3434

35-
public static CloneTableInfo From<TSource, TTarget>(string sourceTableName = null, string targetTableName = null)
35+
public static CloneTableInfo From<TSource, TTarget>(string sourceTableName = null, string targetTableName = null, string targetPrefix = null, string targetSuffix = null)
3636
{
3737
//If the generic type is ISkipMappingLookup then we must have a valid sourceTableName specified as a param...
3838
if (SqlBulkHelpersProcessingDefinition.SkipMappingLookupType.IsAssignableFrom(typeof(TSource)))
@@ -46,22 +46,22 @@ public static CloneTableInfo From<TSource, TTarget>(string sourceTableName = nul
4646
? sourceTableName
4747
: targetTableName;
4848

49-
//If the generic type is ISkipMappingLookup then we must have a valid sourceTableName specified as a param...
49+
//If the generic type is ISkipMappingLookup then we must have a valid validTargetTableName specified as a param...
5050
if (SqlBulkHelpersProcessingDefinition.SkipMappingLookupType.IsAssignableFrom(typeof(TTarget)))
5151
{
5252
//We validate the valid target table name but if it's still blank then we throw an Argument
5353
// exception because no 'targetTableName' could be resolved...
5454
validTargetTableName.AssertArgumentIsNotNullOrWhiteSpace(nameof(targetTableName));
5555
}
5656

57-
var targetTable = TableNameTerm.From<TTarget>(targetTableName ?? sourceTableName);
57+
var targetTable = TableNameTerm.From<TTarget>(targetTableName ?? sourceTableName).ApplyNamePrefixOrSuffix(targetPrefix, targetSuffix);
5858
return new CloneTableInfo(sourceTable, targetTable);
5959
}
6060

61-
public static CloneTableInfo From(string sourceTableName, string targetTableName = null)
62-
=> From<ISkipMappingLookup, ISkipMappingLookup>(sourceTableName, targetTableName);
61+
public static CloneTableInfo From(string sourceTableName, string targetTableName = null, string targetPrefix = null, string targetSuffix = null)
62+
=> From<ISkipMappingLookup, ISkipMappingLookup>(sourceTableName, targetTableName, targetPrefix, targetSuffix);
6363

64-
public static CloneTableInfo ForNewSchema(TableNameTerm sourceTable, string targetSchemaName)
65-
=> new CloneTableInfo(sourceTable, sourceTable.SwitchSchema(targetSchemaName));
64+
public static CloneTableInfo ForNewSchema(TableNameTerm sourceTable, string targetSchemaName, string targetTablePrefix = null, string targetTableSuffix = null)
65+
=> new CloneTableInfo(sourceTable, sourceTable.SwitchSchema(targetSchemaName).ApplyNamePrefixOrSuffix(targetTablePrefix, targetTableSuffix));
6666
}
6767
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using System.Threading.Tasks;
2+
using Microsoft.Data.SqlClient;
23

34
namespace SqlBulkHelpers.MaterializedData
45
{
56
public interface IMaterializeDataContextCompletionSource : IMaterializeDataContext
67
{
7-
Task FinishMaterializeDataProcessAsync();
8+
Task FinishMaterializeDataProcessAsync(SqlTransaction sqlTransaction);
89
}
910
}

NetStandard.SqlBulkHelpers/MaterializedData/MaterializeDataContext.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ namespace SqlBulkHelpers.MaterializedData
99
{
1010
public class MaterializeDataContext : IMaterializeDataContextCompletionSource, IMaterializeDataContext
1111
{
12-
protected SqlTransaction SqlTransaction { get; }
1312
protected ILookup<string, MaterializationTableInfo> TableLookupByFullyQualifiedName { get; }
1413
protected ILookup<string, MaterializationTableInfo> TableLookupByOriginalName { get; }
1514
protected ISqlBulkHelpersConfig BulkHelpersConfig { get; }
@@ -80,16 +79,15 @@ public MaterializationTableInfo FindMaterializationTableInfoCaseInsensitive(Type
8079
/// </summary>
8180
public bool EnableDataConstraintChecksOnCompletion { get; set; } = true;
8281

83-
public MaterializeDataContext(SqlTransaction sqlTransaction, MaterializationTableInfo[] materializationTables, ISqlBulkHelpersConfig bulkHelpersConfig)
82+
public MaterializeDataContext(MaterializationTableInfo[] materializationTables, ISqlBulkHelpersConfig bulkHelpersConfig)
8483
{
85-
SqlTransaction = sqlTransaction.AssertArgumentIsNotNull(nameof(sqlTransaction));
8684
Tables = materializationTables.AssertArgumentIsNotNull(nameof(materializationTables));
8785
BulkHelpersConfig = bulkHelpersConfig.AssertArgumentIsNotNull(nameof(bulkHelpersConfig));
8886
TableLookupByFullyQualifiedName = Tables.ToLookup(t => t.LiveTable.FullyQualifiedTableName, StringComparer.OrdinalIgnoreCase);
8987
TableLookupByOriginalName = Tables.ToLookup(t => t.OriginalTableName, StringComparer.OrdinalIgnoreCase);
9088
}
9189

92-
internal async Task HandleNonTransactionTasksBeforeMaterialization()
90+
internal async Task HandleParallelConnectionTasksBeforeMaterialization()
9391
{
9492
//NOW JUST PRIOR to Executing the Materialized Data Switch we must handle any actions required outside of the Materialized Data Transaction (e.g. FullTextIndexes, etc.)
9593
//NOTE: We do this here so that our live tables have the absolute minimum impact; meaning things like Full Text Indexes are Dropped for ONLY the amount of time it takes to execute our Switch
@@ -118,7 +116,7 @@ await tablesWithFullTextIndexes.ForEachAsync(BulkHelpersConfig.MaxConcurrentConn
118116
/// occurs during the Finish Materialization process!!!
119117
/// </summary>
120118
/// <returns></returns>
121-
internal async Task HandleNonTransactionTasksAfterMaterialization()
119+
internal async Task HandleParallelConnectionTasksAfterMaterialization()
122120
{
123121
if (TablesWithFullTextIndexesRemoved.Any())
124122
{
@@ -134,7 +132,7 @@ await TablesWithFullTextIndexesRemoved.ForEachAsync(BulkHelpersConfig.MaxConcurr
134132
}
135133
}
136134

137-
public async Task FinishMaterializeDataProcessAsync()
135+
public async Task FinishMaterializeDataProcessAsync(SqlTransaction sqlTransaction)
138136
{
139137
var materializationTables = this.Tables;
140138
var switchScriptBuilder = MaterializedDataScriptBuilder.NewSqlScript();
@@ -207,16 +205,10 @@ public async Task FinishMaterializeDataProcessAsync()
207205
//NOTE: FKeys must be explicitly re-enabled to ensure they are restored to Trusted state; they aren't included in the ALL Constraint Check.
208206
.EnableForeignKeyChecks(materializationTableInfo.LiveTable, this.EnableDataConstraintChecksOnCompletion, liveTableDefinition.ForeignKeyConstraints.AsArray())
209207
//Re-enable All other Referencing FKey Checks that were disable above to allow the switching above...
210-
.EnableReferencingForeignKeyChecks(this.EnableDataConstraintChecksOnCompletion, otherReferencingFKeyConstraints.AsArray())
211-
//Finally cleanup the Loading and Discarding tables...
212-
.DropTable(materializationTableInfo.LoadingTable)
213-
.DropTable(materializationTableInfo.DiscardingTable);
214-
208+
.EnableReferencingForeignKeyChecks(this.EnableDataConstraintChecksOnCompletion, otherReferencingFKeyConstraints.AsArray());
215209
}
216210

217-
//var timeoutConvertedToMinutes = Math.Max(1, (int)Math.Ceiling((decimal)BulkHelpersConfig.MaterializeDataStructureProcessingTimeoutSeconds / 60));
218-
219-
await SqlTransaction.ExecuteMaterializedDataSqlScriptAsync(
211+
await sqlTransaction.ExecuteMaterializedDataSqlScriptAsync(
220212
switchScriptBuilder,
221213
BulkHelpersConfig.MaterializeDataStructureProcessingTimeoutSeconds
222214
).ConfigureAwait(false);

0 commit comments

Comments
 (0)