Class SqlBulkCopyLoader<TRecord>
- Namespace
- Wolfgang.Etl.SqlBulkCopy
- Assembly
- Wolfgang.Etl.SqlBulkCopy.dll
Loads items of type TRecord into a SQL Server table
using SqlBulkCopy for high-throughput bulk insert operations.
public sealed class SqlBulkCopyLoader<TRecord> : LoaderBase<TRecord, SqlBulkCopyReport>, ILoadWithProgressAndCancellationAsync<TRecord, SqlBulkCopyReport>, ILoadWithProgressAsync<TRecord, SqlBulkCopyReport>, ILoadWithCancellationAsync<TRecord>, ILoadAsync<TRecord>, IReportsItemErrors, IAsyncDisposable, IDisposable where TRecord : notnull
Type Parameters
TRecordThe type of items to load. Must be
notnull.
- Inheritance
-
LoaderBase<TRecord, SqlBulkCopyReport>SqlBulkCopyLoader<TRecord>
- Implements
-
ILoadWithProgressAndCancellationAsync<TRecord, SqlBulkCopyReport>ILoadWithProgressAsync<TRecord, SqlBulkCopyReport>ILoadWithCancellationAsync<TRecord>ILoadAsync<TRecord>IReportsItemErrors
- Inherited Members
-
LoaderBase<TRecord, SqlBulkCopyReport>.DisposeAsync()LoaderBase<TRecord, SqlBulkCopyReport>.Dispose()LoaderBase<TRecord, SqlBulkCopyReport>.ReportingIntervalLoaderBase<TRecord, SqlBulkCopyReport>.CurrentItemCountLoaderBase<TRecord, SqlBulkCopyReport>.CurrentSkippedItemCountLoaderBase<TRecord, SqlBulkCopyReport>.CurrentErrorItemCountLoaderBase<TRecord, SqlBulkCopyReport>.MaximumItemCountLoaderBase<TRecord, SqlBulkCopyReport>.SkipItemCountLoaderBase<TRecord, SqlBulkCopyReport>.WorkerResilienceLoaderBase<TRecord, SqlBulkCopyReport>.ErrorPolicy
Examples
var loader = new SqlBulkCopyLoader<Person>
(
connection,
new SqlBulkCopyLoaderOptions<Person>
{
BatchSize = 5000,
BulkCopyTimeout = 60
}
);
await loader.LoadAsync(items, cancellationToken);
Remarks
Maps .NET types to SQL Server tables using System.ComponentModel.DataAnnotations.Schema
attributes: [Table], [Column], and [NotMapped].
Supports nested collection properties that map to separate child tables, optional pre/post-load actions, and opt-in data validation.
Constructors
SqlBulkCopyLoader(SqlConnection)
Initializes a new instance of the SqlBulkCopyLoader<TRecord> class.
public SqlBulkCopyLoader(SqlConnection connection)
Parameters
connectionSqlConnectionThe SQL Server connection.
Exceptions
- ArgumentNullException
Thrown when
connectionisnull.
SqlBulkCopyLoader(SqlConnection, SqlBulkCopyOptions, SqlTransaction?, ILogger<SqlBulkCopyLoader<TRecord>>?)
Initializes a new instance of the SqlBulkCopyLoader<TRecord> class with full configuration.
public SqlBulkCopyLoader(SqlConnection connection, SqlBulkCopyOptions options, SqlTransaction? transaction, ILogger<SqlBulkCopyLoader<TRecord>>? logger = null)
Parameters
connectionSqlConnectionThe SQL Server connection.
optionsSqlBulkCopyOptionsThe bulk copy options.
transactionSqlTransactionAn optional external transaction.
loggerILogger<SqlBulkCopyLoader<TRecord>>An optional logger instance for diagnostic output.
Examples
// One transaction spanning every file: either every file lands or none
// do (roll back on any failure).
using var transaction = connection.BeginTransaction();
try
{
foreach (var file in files)
{
var loader = new SqlBulkCopyLoader<Person>(connection, SqlBulkCopyOptions.Default, transaction)
{
DestinationTableName = "People"
};
await loader.LoadAsync(file, cancellationToken);
}
transaction.Commit();
}
catch
{
// Roll back, but don't let a rollback failure mask the original error.
try
{
transaction.Rollback();
}
catch
{
// Ignore: rethrow the original load exception below.
}
throw;
}
// Commit after each file: a mid-run failure keeps the files already
// committed (good for large, independent files and restartability).
foreach (var file in files)
{
using var transaction = connection.BeginTransaction();
var loader = new SqlBulkCopyLoader<Person>(connection, SqlBulkCopyOptions.Default, transaction)
{
DestinationTableName = "People"
};
await loader.LoadAsync(file, cancellationToken);
transaction.Commit();
}
Exceptions
- ArgumentNullException
Thrown when
connectionisnull.
SqlBulkCopyLoader(SqlConnection, ILogger<SqlBulkCopyLoader<TRecord>>?)
Initializes a new instance of the SqlBulkCopyLoader<TRecord> class with diagnostic logging.
public SqlBulkCopyLoader(SqlConnection connection, ILogger<SqlBulkCopyLoader<TRecord>>? logger = null)
Parameters
connectionSqlConnectionThe SQL Server connection.
loggerILogger<SqlBulkCopyLoader<TRecord>>An optional logger instance for diagnostic output. When
null— or omitted — Instance is used and logging is disabled.
Exceptions
- ArgumentNullException
Thrown when
connectionisnull.
SqlBulkCopyLoader(SqlConnection, SqlBulkCopyLoaderOptions<TRecord>?, SqlTransaction?, ILogger<SqlBulkCopyLoader<TRecord>>?)
Initializes a new instance of the SqlBulkCopyLoader<TRecord> class configured through an options record (ADR-0009). This is the initialization path every other constructor chains into.
public SqlBulkCopyLoader(SqlConnection connection, SqlBulkCopyLoaderOptions<TRecord>? options = null, SqlTransaction? transaction = null, ILogger<SqlBulkCopyLoader<TRecord>>? logger = null)
Parameters
connectionSqlConnectionThe open SqlConnection to bulk-copy into. The caller owns its lifetime.
optionsSqlBulkCopyLoaderOptions<TRecord>The loader's configuration, including the settings inherited from Wolfgang.Etl.Abstractions.LoaderOptions. When null — or omitted — every documented default applies.
transactionSqlTransactionAn optional SqlTransaction the bulk copy and the pre/post actions run in.
loggerILogger<SqlBulkCopyLoader<TRecord>>An optional logger. When null — or omitted — Instance is used.
Exceptions
- ArgumentNullException
connectionis null.- ArgumentOutOfRangeException
BatchSize is below
1or BulkCopyTimeout is negative.
Properties
BatchSize
Gets or sets the number of rows in each batch sent to the server.
public int BatchSize { get; set; }
Property Value
- int
The default is 10,000.
Exceptions
- ArgumentOutOfRangeException
Thrown when the value is less than 1.
BulkCopyTimeout
Gets or sets the timeout in seconds for each bulk copy operation. A value of 0 means no timeout.
public int BulkCopyTimeout { get; set; }
Property Value
- int
The default is 30 seconds.
Exceptions
- ArgumentOutOfRangeException
Thrown when the value is negative.
DestinationSchemaName
Gets or sets an optional destination schema name override.
When null, the schema is derived from the [Table] attribute.
public string? DestinationSchemaName { get; set; }
Property Value
DestinationTableName
Gets or sets an optional destination table name override.
When null, the table name is derived from the [Table] attribute
or the type name.
public string? DestinationTableName { get; set; }
Property Value
EnableDataValidation
Gets or sets a value indicating whether to validate each item using System.ComponentModel.DataAnnotations attributes before loading.
public bool EnableDataValidation { get; set; }
Property Value
- bool
The default is
false.
Remarks
Enabling validation adds per-item overhead. Validation is applied recursively
to root TRecord instances and to every level of
nested-collection children. How a failure is handled is controlled by
ValidationFailureBehavior — the default is to throw a
SqlBulkCopyValidationException, which fails loudly and
stops the load. Set ValidationFailureBehavior to
Skip
to tolerate dirty data and drop only the failing items.
IsDryRun
Gets or sets a value indicating whether the load runs as a dry run — validating the pipeline against real data without writing to SQL Server.
public bool IsDryRun { get; set; }
Property Value
- bool
The default is
false.
Remarks
When true, the loader still enumerates the source, applies
SkipItemCount / MaximumItemCount, runs data
validation, increments the progress counters, and logs as usual — but
performs no SQL side effects: the PreAction /
PostAction (e.g. truncate / delete) and the bulk insert are
all skipped. This lets a caller confirm a pipeline runs end-to-end and
surfaces mapping / validation errors without touching the destination.
OnNestedValidationFailed
Gets or sets an optional callback invoked when a nested-collection child instance fails validation.
public Action<object, ICollection<ValidationResult>>? OnNestedValidationFailed { get; set; }
Property Value
Remarks
Only invoked when EnableDataValidation is true.
The callback runs before the configured
ValidationFailureBehavior takes effect. The child is
passed as object because the child type is resolved at
load time, not at TRecord definition. For
root-item validation, see OnValidationFailed.
OnValidationFailed
Gets or sets an optional callback invoked when a root
TRecord fails validation.
public Action<TRecord, ICollection<ValidationResult>>? OnValidationFailed { get; set; }
Property Value
- Action<TRecord, ICollection<ValidationResult>>
Remarks
Only invoked when EnableDataValidation is true.
The callback runs before the configured
ValidationFailureBehavior takes effect, so consumers
can log / inspect the failing item from a single hook regardless of
whether the loader then throws or skips. The callback receives the
failing root item and the collection of validation errors. For
nested-collection children, see OnNestedValidationFailed.
PostAction
Gets or sets the action to execute after loading completes.
public PostAction PostAction { get; set; }
Property Value
- PostAction
The default is None.
PostLoadCustomAction
Gets or sets the custom delegate to invoke when PostAction is CustomAction.
public Func<PostLoadActionParameters, Task>? PostLoadCustomAction { get; set; }
Property Value
PreAction
Gets or sets the action to execute before loading begins.
public PreAction PreAction { get; set; }
Property Value
PreLoadCustomAction
Gets or sets the custom delegate to invoke when PreAction is CustomAction.
public Func<PreLoadActionParameters, Task>? PreLoadCustomAction { get; set; }
Property Value
ValidationFailureBehavior
Gets or sets how the loader reacts to a validation failure when
EnableDataValidation is true.
public ValidationFailureBehavior ValidationFailureBehavior { get; set; }
Property Value
- ValidationFailureBehavior
The default is Throw.
Remarks
See ValidationFailureBehavior
for the semantics of each option. The same setting applies to both
root TRecord instances and nested-collection
children.
Methods
CreateProgressReport()
Creates a progress report of type TProgress. This gives the derived class the opportunity to implement a custom progress report that is specific to the loading process.
protected override SqlBulkCopyReport CreateProgressReport()
Returns
- SqlBulkCopyReport
Progress of type TProgress
CreateProgressTimer(IProgress<SqlBulkCopyReport>)
Creates the Wolfgang.Etl.Abstractions.IProgressTimer used to drive progress callbacks. Override this method in a derived class to inject a custom timer (for example, a custom implementation that allows manual control in unit tests).
protected override IProgressTimer CreateProgressTimer(IProgress<SqlBulkCopyReport> progress)
Parameters
progressIProgress<SqlBulkCopyReport>The progress sink that will receive callbacks.
Returns
- IProgressTimer
A started Wolfgang.Etl.Abstractions.IProgressTimer instance.
LoadWorkerAsync(IAsyncEnumerable<TRecord>, CancellationToken)
This method is the core implementation of the loading logic and should be overridden by derived classes.
protected override Task LoadWorkerAsync(IAsyncEnumerable<TRecord> items, CancellationToken token)
Parameters
itemsIAsyncEnumerable<TRecord>The items to be loaded to the destination.
tokenCancellationTokenA CancellationToken to observe while waiting for the task to complete.
Returns
- Task
A task representing the asynchronous operation.
Remarks
Items may be an empty sequence if no data is available or if the loading fails.
Exceptions
- ArgumentNullException
Argument items is null