feat(datasync): extend DbBulkMergeDestination with excludeFromUpdate and updateCondition

This commit is contained in:
Joseph Doherty
2026-01-06 13:35:25 -05:00
parent 61f927bd0e
commit 8af4f9915f
2 changed files with 206 additions and 2 deletions
@@ -24,6 +24,8 @@ public class DbBulkMergeDestination : IImportDestination
private readonly string _tableName;
private readonly string[] _matchColumns;
private readonly string[]? _updateColumns;
private readonly string[]? _excludeFromUpdate;
private readonly string? _updateCondition;
private readonly int _batchSize;
private readonly int _commandTimeoutSeconds;
@@ -37,6 +39,8 @@ public class DbBulkMergeDestination : IImportDestination
/// <param name="tableName">Name of the destination table.</param>
/// <param name="matchColumns">Columns to match on for determining existing rows (key columns).</param>
/// <param name="updateColumns">Columns to update when a row matches. If null, all non-match columns are updated.</param>
/// <param name="excludeFromUpdate">Columns to exclude from the UPDATE clause. Takes precedence over updateColumns.</param>
/// <param name="updateCondition">Optional SQL condition to add to the WHEN MATCHED clause (e.g., "source.LastUpdate > target.LastUpdate").</param>
/// <param name="batchSize">Number of rows per batch. 0 uses the default (10000).</param>
/// <param name="commandTimeoutSeconds">Command timeout in seconds. 0 uses the default (600).</param>
public DbBulkMergeDestination(
@@ -44,6 +48,8 @@ public class DbBulkMergeDestination : IImportDestination
string tableName,
string[] matchColumns,
string[]? updateColumns = null,
string[]? excludeFromUpdate = null,
string? updateCondition = null,
int batchSize = 0,
int commandTimeoutSeconds = 0)
{
@@ -57,6 +63,8 @@ public class DbBulkMergeDestination : IImportDestination
_tableName = tableName;
_matchColumns = matchColumns;
_updateColumns = updateColumns;
_excludeFromUpdate = excludeFromUpdate;
_updateCondition = updateCondition;
_batchSize = batchSize > 0 ? batchSize : DefaultBatchSize;
_commandTimeoutSeconds = commandTimeoutSeconds > 0 ? commandTimeoutSeconds : DefaultCommandTimeoutSeconds;
}
@@ -101,8 +109,12 @@ public class DbBulkMergeDestination : IImportDestination
// Determine update columns (all non-match columns if not specified), filtered to destColumns
var matchSet = new HashSet<string>(_matchColumns, StringComparer.OrdinalIgnoreCase);
var excludeSet = _excludeFromUpdate != null
? new HashSet<string>(_excludeFromUpdate, StringComparer.OrdinalIgnoreCase)
: new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var updateCols = (_updateColumns ?? allColumns.Where(c => !matchSet.Contains(c)).ToArray())
.Where(c => destColumns.Contains(c))
.Where(c => destColumns.Contains(c) && !excludeSet.Contains(c))
.ToArray();
// Build MERGE SQL
@@ -221,7 +233,14 @@ public class DbBulkMergeDestination : IImportDestination
if (updateColumns.Count > 0)
{
sb.AppendLine("WHEN MATCHED THEN UPDATE SET");
if (!string.IsNullOrWhiteSpace(_updateCondition))
{
sb.AppendLine($"WHEN MATCHED AND {_updateCondition} THEN UPDATE SET");
}
else
{
sb.AppendLine("WHEN MATCHED THEN UPDATE SET");
}
sb.AppendLine(string.Join(", ", updateColumns.Select(c => $"target.[{c}] = source.[{c}]")));
}