Class FixedWidthLoader<TRecord>
- Namespace
- Wolfgang.Etl.FixedWidth
- Assembly
- Wolfgang.Etl.FixedWidth.dll
Writes records of type TRecord to a fixed-width text stream
as an asynchronous operation.
public class FixedWidthLoader<TRecord> : LoaderBase<TRecord, FixedWidthReport>, ILoadWithProgressAndCancellationAsync<TRecord, FixedWidthReport>, ILoadWithProgressAsync<TRecord, FixedWidthReport>, ILoadWithCancellationAsync<TRecord>, ILoadAsync<TRecord>, IReportsItemErrors, IAsyncDisposable, IDisposable where TRecord : notnull
Type Parameters
TRecord
- Inheritance
-
LoaderBase<TRecord, FixedWidthReport>FixedWidthLoader<TRecord>
- Implements
-
ILoadWithProgressAndCancellationAsync<TRecord, FixedWidthReport>ILoadWithProgressAsync<TRecord, FixedWidthReport>ILoadWithCancellationAsync<TRecord>ILoadAsync<TRecord>IReportsItemErrors
- Inherited Members
-
LoaderBase<TRecord, FixedWidthReport>.CreateProgressReport()LoaderBase<TRecord, FixedWidthReport>.IncrementCurrentItemCount()LoaderBase<TRecord, FixedWidthReport>.IncrementCurrentSkippedItemCount()LoaderBase<TRecord, FixedWidthReport>.OnItemError(ItemErrorContext)LoaderBase<TRecord, FixedWidthReport>.HandleItemError(ItemErrorContext)LoaderBase<TRecord, FixedWidthReport>.DisposeAsync()LoaderBase<TRecord, FixedWidthReport>.Dispose()LoaderBase<TRecord, FixedWidthReport>.StartedAtLoaderBase<TRecord, FixedWidthReport>.ElapsedLoaderBase<TRecord, FixedWidthReport>.ReportingIntervalLoaderBase<TRecord, FixedWidthReport>.CurrentItemCountLoaderBase<TRecord, FixedWidthReport>.CurrentSkippedItemCountLoaderBase<TRecord, FixedWidthReport>.CurrentErrorItemCountLoaderBase<TRecord, FixedWidthReport>.MaximumItemCountLoaderBase<TRecord, FixedWidthReport>.SkipItemCountLoaderBase<TRecord, FixedWidthReport>.WorkerResilienceLoaderBase<TRecord, FixedWidthReport>.ErrorPolicy
Remarks
Two construction modes are supported, each with different ownership semantics:
- TextWriter constructor — the caller owns the TextWriter lifetime. The loader does not dispose it, and calling Dispose() is optional (no-op). The caller is responsible for flushing the writer.
- Stream constructor — the loader creates an internal
StreamWriter with a 64 KB buffer for improved throughput.
The caller retains ownership of the Stream (it is not closed).
The internal writer is flushed automatically at the end of
LoadWorkerAsync, and Dispose() must be called to release it.
// Stream-based (preferred for files — 64 KB buffer reduces syscall overhead):
await using var stream = File.OpenWrite("output.txt");
using var loader = new FixedWidthLoader<MyRecord>(stream);
// TextWriter-based (caller owns the writer):
var sw = new StringWriter();
var loader = new FixedWidthLoader<MyRecord>(sw);
var result = sw.ToString();
// Write a formatted table to the console
var loader = new FixedWidthLoader<MyRecord>(Console.Out, new FixedWidthLoaderOptions
{
WriteHeader = true,
FieldSeparator = '-',
FieldDelimiter = " | ",
});
Constructors
FixedWidthLoader(Stream, ILogger<FixedWidthLoader<TRecord>>, Encoding)
Initializes a new FixedWidthLoader<TRecord> from a Stream decoded with the supplied Encoding, with diagnostic logging.
[Obsolete("Use the constructor that takes FixedWidthLoaderStreamOptions. This overload will be removed in a future release.")]
public FixedWidthLoader(Stream stream, ILogger<FixedWidthLoader<TRecord>> logger, Encoding encoding)
Parameters
streamStreamThe stream to use.
loggerILogger<FixedWidthLoader<TRecord>>The logger to use for diagnostic output.
encodingEncodingThe encoding to decode with, or null for the documented default.
Exceptions
- ArgumentNullException
streamis null.
FixedWidthLoader(Stream, Encoding)
Initializes a new FixedWidthLoader<TRecord> from a Stream decoded with the supplied Encoding.
[Obsolete("Use the constructor that takes FixedWidthLoaderStreamOptions. This overload will be removed in a future release.")]
public FixedWidthLoader(Stream stream, Encoding encoding)
Parameters
streamStreamThe stream to use.
encodingEncodingThe encoding to decode with, or null for the documented default.
Exceptions
- ArgumentNullException
streamis null.
FixedWidthLoader(Stream, FixedWidthLoaderStreamOptions?, ILogger<FixedWidthLoader<TRecord>>?)
Initializes a new FixedWidthLoader<TRecord> over the specified Stream, with the logger as the trailing optional parameter.
public FixedWidthLoader(Stream stream, FixedWidthLoaderStreamOptions? options = null, ILogger<FixedWidthLoader<TRecord>>? logger = null)
Parameters
streamStreamThe Stream to use.
optionsFixedWidthLoaderStreamOptionsOptions that control behaviour, including the Encoding to use. When
null, the documented defaults apply.loggerILogger<FixedWidthLoader<TRecord>>An optional logger for diagnostic output. When
null— or omitted — Instance is used and logging is disabled.
Exceptions
- ArgumentNullException
streamis null.
FixedWidthLoader(TextWriter, ILogger<FixedWidthLoader<TRecord>>?)
Initializes a new FixedWidthLoader<TRecord> that writes to the specified TextWriter with diagnostic logging.
public FixedWidthLoader(TextWriter writer, ILogger<FixedWidthLoader<TRecord>>? logger = null)
Parameters
writerTextWriterThe TextWriter to write fixed-width records to.
loggerILogger<FixedWidthLoader<TRecord>>The logger instance for diagnostic output.
Exceptions
- ArgumentNullException
writerorloggeris null.
FixedWidthLoader(TextWriter, FixedWidthLoaderOptions?, ILogger<FixedWidthLoader<TRecord>>?)
Initializes a new instance that writes to writer with the given configuration.
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Shipped shape: the Stream overload keeps its optional options/logger for source compatibility until the constructor set is settled in the 2026-12-15 removal wave (#373 / #343).")]
public FixedWidthLoader(TextWriter writer, FixedWidthLoaderOptions? options = null, ILogger<FixedWidthLoader<TRecord>>? logger = null)
Parameters
writerTextWriterThe writer receiving the fixed-width lines. The caller owns it.
optionsFixedWidthLoaderOptionsThe formatting configuration. null (the default) keeps every default. The writer already owns its encoding, so this is the base record without an
Encoding.loggerILogger<FixedWidthLoader<TRecord>>An optional logger.
Exceptions
- ArgumentNullException
writeris null.
Properties
CurrentLineNumber
The 1-based physical line number of the line most recently written to the output. Updated after each line is written. Includes the header line and separator line if written. Matches the line number shown in a text editor.
public long CurrentLineNumber { get; }
Property Value
Remarks
Thread-safe: reads are performed with Interlocked so this property may be sampled from a progress-reporting timer thread without a data race.
FieldDelimiter
An optional string written between fields on every line including headers,
separators, and data rows. Set to null (default) for pure
fixed-width output with no delimiter. Use a value like " | " for
human-readable report output.
public string? FieldDelimiter { get; set; }
Property Value
Examples
// Supplied through FixedWidthLoaderOptions, passed to the constructor:
FieldDelimiter = " | ", // human-readable table: "John | Smith | 42 "
FieldDelimiter = null, // pure fixed-width (default): "John Smith 42 "
Remarks
When set, the delimiter is inserted between every adjacent pair of fields — it is not appended after the last field. The FieldDelimiter on the corresponding extractor must be set to the same value so that field boundaries are correctly identified during extraction.
FieldSeparator
When non-null, a separator line is written after the header, consisting of the specified character repeated to each field's width. Set to null (default) to write no separator. Has no effect if WriteHeader is false. Mirrors FieldSeparator.
public char? FieldSeparator { get; set; }
Property Value
- char?
Examples
// Supplied through FixedWidthLoaderOptions, passed to the constructor:
FieldSeparator = '-', // writes "----------"
FieldSeparator = '=', // writes "=========="
FieldSeparator = null, // no separator (default)
HeaderConverter
The function used to convert a header label to its string representation. Defaults to StrictHeader.
public Func<string, FieldContext, string> HeaderConverter { get; set; }
Property Value
Examples
// Render all headers in upper-case:
new FixedWidthLoaderOptions
{
HeaderConverter = (label, ctx) =>
FixedWidthConverter.StrictHeader(label.ToUpperInvariant(), ctx),
}
// Silently truncate headers that are too long:
new FixedWidthLoaderOptions { HeaderConverter = FixedWidthConverter.TruncateHeader }
Remarks
Only called when WriteHeader is true. Space-padding to FieldLength is applied by the framework after this converter returns — the converter must only ensure the returned string is not longer than the field width.
IsDryRun
Gets or sets a value indicating whether the run is a dry run that exercises the pipeline without writing any output.
public bool IsDryRun { get; set; }
Property Value
Remarks
When true, the loader enumerates the source and evaluates Wolfgang.Etl.Abstractions.LoaderBase<TDestination, TProgress>.SkipItemCount / Wolfgang.Etl.Abstractions.LoaderBase<TDestination, TProgress>.MaximumItemCount, increments progress counters, fires the progress-timer callback, and logs as usual — but writes nothing to the output fixed-width stream. Field-width validation still runs, so a dry run surfaces FieldOverflowException the same way a real run would. Defaults to false.
Schema
An optional layout that overrides the [FixedWidthField] / [FixedWidthSkip] attributes
on TRecord (#23). Build one with FixedWidthSchemaBuilder<T> to
map a type you cannot decorate, or to define the layout in code. When null (the
default) the attribute-based layout is used. The schema's RecordType
must be TRecord.
public FixedWidthSchema? Schema { get; set; }
Property Value
ValueConverter
The function used to convert a field value to its string representation before padding and writing. Defaults to Strict.
public Func<object, FieldContext, string> ValueConverter { get; set; }
Property Value
Examples
// Write booleans as "Y"/"N" instead of "True"/"False":
new FixedWidthLoaderOptions
{
ValueConverter = (value, ctx) =>
ctx.PropertyType == typeof(bool)
? ((bool)value ? "Y" : "N")
: FixedWidthConverter.Strict(value, ctx),
}
// Silently truncate all values instead of throwing on overflow:
new FixedWidthLoaderOptions { ValueConverter = FixedWidthConverter.Truncate }
Remarks
The converter receives the raw boxed property value and a FieldContext describing the field. It must return a string no longer than FieldLength; otherwise the loader's field-width safety-net will throw a FieldOverflowException.
WriteHeader
public bool WriteHeader { get; set; }
Property Value
Examples
// Supplied through FixedWidthLoaderOptions, passed to the constructor:
WriteHeader = true,
// Produces a header line like: "FirstName LastName Age "
Remarks
The header label for each field is taken from Header if set, or the property name otherwise. Labels are passed through HeaderConverter before being written.
Methods
CreateProgressReport()
Creates a progress report snapshot for the current loader state.
protected override FixedWidthReport CreateProgressReport()
Returns
- FixedWidthReport
A FixedWidthReport snapshot containing Wolfgang.Etl.Abstractions.LoaderBase<TDestination, TProgress>.CurrentItemCount, Wolfgang.Etl.Abstractions.LoaderBase<TDestination, TProgress>.CurrentSkippedItemCount, and CurrentLineNumber at the moment of the call.
CreateProgressTimer(IProgress<FixedWidthReport>)
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<FixedWidthReport> progress)
Parameters
progressIProgress<FixedWidthReport>The progress sink that will receive callbacks.
Returns
- IProgressTimer
A started Wolfgang.Etl.Abstractions.IProgressTimer instance.
Dispose(bool)
Releases the internal StreamWriter when this instance was constructed from a Stream, then defers to the base class. Has no effect on a caller-owned TextWriter.
protected override void Dispose(bool disposing)
Parameters
disposingbooltrue when called from IDisposable; false when called from a finalizer.
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