Table of Contents

Class SheetParallel

Namespace
ExcelReader.Core.Reader
Assembly
ExcelReader.Core.dll

Reads a sheet of any format across several threads: ParseAsync yields typed rows in source order, ForEachAsync hands each row to a callback on the worker threads, and AggregateAsync folds rows into one accumulator per partition.

public static class SheetParallel
Inheritance
SheetParallel
Inherited Members

Remarks

The caller owns the workbook and keeps it open until the returned task completes or the enumeration ends. The CSV dialect is the CsvReaderOptions the workbook was opened with.

A read falls back to one sequential pass, with the same results and no error, when DegreeOfParallelism is 1, the sheet is too small to partition usefully, the sheet is an XLS sheet or a table view, or a CSV cannot be partitioned (a stream that is not a file or a non-UTF-8 encoding).

Delivery guarantees. The general contract is the weaker CSV one: ForEachAsync is at-least-once, and a partition whose start was guessed wrongly is discarded and read again. XLSB delivers every row exactly once, in ForEachAsync too. XLSX delivers exactly once for a well-formed sheet; a malformed one can make ForEachAsync deliver a row twice. ParseAsync yields the same items in the same order as the sequential ExcelParser<T> on every format, and AggregateAsync and ForEachAsync equal their own single-thread run.

Row: ParseAsync renumbers it from the count emitted at the merge; ForEachAsync and AggregateAsync report the exact row when sequential and 0 when partitioned.

Methods

AggregateAsync<TState>(IExcelSheet, RowAggregation<TState>, SheetParallelOptions?, CancellationToken)

Reads a sheet across several threads and folds every row with the functions of aggregation, one accumulator per partition, combined in sheet order.

public static Task<TState> AggregateAsync<TState>(IExcelSheet sheet, RowAggregation<TState> aggregation, SheetParallelOptions? options = null, CancellationToken ct = default)

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the task completes.

aggregation RowAggregation<TState>

The seed, accumulate and combine functions. They carry the same contract as IRowAccumulator<TSelf, TModel>.

options SheetParallelOptions

Parallelism and header options. Defaults to Default.

ct CancellationToken

A token to cancel processing.

Returns

Task<TState>

The combined accumulator.

Type Parameters

TState

The accumulator type.

Remarks

Carries the same fallbacks as the IRowAccumulator<TSelf, TModel> overloads.

Exceptions

ArgumentNullException

sheet or aggregation, or one of its functions, is null.

ArgumentOutOfRangeException

A numeric option is negative.

AggregateAsync<TAccumulator, TModel>(IExcelSheet, ExcelParser<TModel>, SheetParallelOptions?, CancellationToken)

Reads a sheet across several threads, binds every row to a TModel through parser and folds it into a TAccumulator, one instance per partition, merged in sheet order.

public static Task<TAccumulator> AggregateAsync<TAccumulator, TModel>(IExcelSheet sheet, ExcelParser<TModel> parser, SheetParallelOptions? options = null, CancellationToken ct = default) where TAccumulator : IRowAccumulator<TAccumulator, TModel>, new() where TModel : allows ref struct

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the task completes.

parser ExcelParser<TModel>

How columns bind to TModel; created with ExcelParser. Its HeaderRow is ignored in favour of HeaderRow.

options SheetParallelOptions

Parallelism and header options. Defaults to Default.

ct CancellationToken

A token to cancel processing.

Returns

Task<TAccumulator>

The accumulator holding every record.

Type Parameters

TAccumulator

The accumulator type. See IRowAccumulator<TSelf, TModel> for the contract it must honor.

TModel

The model type.

Remarks

The header is bound once, before any row is folded. A property that fails to parse keeps its default unless ThrowOnParseFailure is set; a missing [ExcelRequired] column or value throws ExcelParseException; empty rows are skipped.

Row is exact when the sheet is read sequentially and 0 when it is partitioned, since a partition does not know how many rows precede it.

Otherwise carries the same contract and fallbacks as the IRowRecord<TSelf> overload.

Exceptions

ArgumentNullException

sheet or parser is null.

ArgumentException

parser binds columns by header name and HeaderRow is 0.

ArgumentOutOfRangeException

A numeric option is negative.

AggregateAsync<TAccumulator, TRecord>(IExcelSheet, SheetParallelOptions?, CancellationToken)

Reads a sheet across several threads, parses every row into a TRecord and folds it into a TAccumulator, one instance per partition, merged in sheet order.

public static Task<TAccumulator> AggregateAsync<TAccumulator, TRecord>(IExcelSheet sheet, SheetParallelOptions? options = null, CancellationToken ct = default) where TAccumulator : IRowAccumulator<TAccumulator, TRecord>, new() where TRecord : IRowRecord<TRecord>, allows ref struct

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the task completes.

options SheetParallelOptions

Parallelism and header options. Defaults to Default.

ct CancellationToken

A token to cancel processing.

Returns

Task<TAccumulator>

The accumulator holding every record.

Type Parameters

TAccumulator

The accumulator type. See IRowAccumulator<TSelf, TModel> for the contract it must honor.

TRecord

The record type. A record whose TryParse(Row, out TSelf) returns false is skipped.

Remarks

A partition read from a wrongly guessed start is read again into a new accumulator. The result holds every record exactly once, but TryParse(Row, out TSelf) and Add(TModel) may already have run for the discarded pass's records, as IRowAccumulator<TSelf, TModel> describes. A sequential fallback uses a single accumulator and never calls Merge(TSelf).

Exceptions

ArgumentNullException

sheet is null.

ArgumentOutOfRangeException

A numeric option is negative.

ForEachAsync<TModel>(IExcelSheet, ExcelParser<TModel>, Action<TModel>, SheetParallelOptions?, CancellationToken)

Reads a sheet across several threads, binds every row to a TModel through parser and hands it to body.

public static Task ForEachAsync<TModel>(IExcelSheet sheet, ExcelParser<TModel> parser, Action<TModel> body, SheetParallelOptions? options = null, CancellationToken ct = default) where TModel : allows ref struct

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the task completes.

parser ExcelParser<TModel>

How columns bind to TModel; created with ExcelParser. Its HeaderRow is ignored in favour of HeaderRow.

body Action<TModel>

Receives each record. See the remarks for the contract it must honor.

options SheetParallelOptions

Parallelism and header options. Defaults to Default.

ct CancellationToken

A token to cancel processing.

Returns

Task

A task that completes when every record has been delivered.

Type Parameters

TModel

The model type. May be a ref struct with ReadOnlySpan<T> of byte properties.

Remarks

Carries the same concurrency, delivery and span-lifetime contract as the IRowRecord<TSelf> overload: body runs concurrently, delivery is at least once in general (exactly once on XLSB, and on a well-formed XLSX sheet), and records arrive in no particular order. A property that fails to parse keeps its default unless ThrowOnParseFailure is set; a missing [ExcelRequired] column or value throws ExcelParseException; empty rows are skipped.

Row is exact when the sheet is read sequentially and 0 when it is partitioned, since a partition does not know how many rows precede it.

Exceptions

ArgumentNullException

sheet, parser or body is null.

ArgumentException

parser binds columns by header name and HeaderRow is 0.

ArgumentOutOfRangeException

A numeric option is negative.

ForEachAsync<TRecord>(IExcelSheet, Action<TRecord>, SheetParallelOptions?, CancellationToken)

Reads a sheet across several threads, parses every row into a TRecord and hands it to body.

public static Task ForEachAsync<TRecord>(IExcelSheet sheet, Action<TRecord> body, SheetParallelOptions? options = null, CancellationToken ct = default) where TRecord : IRowRecord<TRecord>, allows ref struct

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the task completes.

body Action<TRecord>

Receives each record. See the remarks for the contract it must honor.

options SheetParallelOptions

Parallelism and header options. Defaults to Default.

ct CancellationToken

A token to cancel processing.

Returns

Task

A task that completes when every record has been delivered.

Type Parameters

TRecord

The record type. A record whose TryParse(Row, out TSelf) returns false is skipped.

Remarks

body runs concurrently on worker threads and must be safe to call from several at once; the caller owns any synchronization. Records arrive in no particular order.

Delivery is at least once in general: body may be invoked more than once for the same record. On CSV, a partition whose start was guessed wrongly is read again from the confirmed offset, and the discarded speculative pass may already have delivered a whole partition's worth of records, which may also be misparsed: a wrong start shifts every field boundary. An exception body throws during a discarded pass is discarded with it. A start is only guessed wrongly inside a quoted field, so a CSV in which the quote character never appears delivers every record exactly once. XLSB delivers every row exactly once. XLSX does too for a well-formed sheet; on a malformed one, workers may already have called body for rows that are then dropped, so a row can be delivered twice or be one the sequential reader would not produce.

A body with side effects must therefore be idempotent and tolerate records that do not exist. For exactly-once on any source, use AggregateAsync<TAccumulator, TRecord>(IExcelSheet, SheetParallelOptions?, CancellationToken), where the discarded partition's accumulator is thrown away, and apply the side effects once it returns.

A ReadOnlySpan<T> column points into a pooled buffer the worker reuses once body returns. Copy anything that must outlive the call.

Exceptions

ArgumentNullException

sheet or body is null.

ArgumentOutOfRangeException

A numeric option is negative.

ParseAsync<T>(IExcelSheet, ExcelParser<T>, SheetParallelOptions?, CancellationToken)

Parses a sheet into T instances across several threads, yielding the same sequence, in the same order, that the sequential ExcelParser<T> produces.

public static IAsyncEnumerable<T> ParseAsync<T>(IExcelSheet sheet, ExcelParser<T> parser, SheetParallelOptions? options = null, CancellationToken ct = default)

Parameters

sheet IExcelSheet

The sheet to read. The caller keeps ownership and keeps its workbook open until the enumeration ends.

parser ExcelParser<T>

How columns bind to T; created with ExcelParser. Its HeaderRow locates the header.

options SheetParallelOptions

Parallelism options. Defaults to Default. HeaderRow does not apply here and must be left at 0.

ct CancellationToken

A token to cancel enumeration.

Returns

IAsyncEnumerable<T>

An enumerable that yields one T per data row.

Type Parameters

T

The row model type to bind each row to.

Remarks

Parsing and type conversion run in parallel; whatever the caller does per row does not. A caller whose per-row work dominates will see little gain from raising DegreeOfParallelism. For CSV, ExcelReader.Core.Reader.Csv.CsvReaderOptions.InternStrings yields substantially less here than sequentially, since each partition keeps its own dedup cache. A partition read from a wrongly guessed start is discarded before any of its rows are yielded, but its cells have already passed through the parser's converters and property setters, which must therefore be free of side effects.

Exceptions

ArgumentNullException

sheet or parser is null.

ArgumentException

HeaderRow is not 0.

ArgumentOutOfRangeException

A numeric option is negative.