Class ExcelReader
- Namespace
- SheetStream.Reading
- Assembly
- SheetStream.dll
Asynchronous reading: the counterpart of Worksheet.LoadFromAsyncEnumerable. An upload can be
buffered with OpenAsync(Stream, bool, CancellationToken) and its rows pulled with
ReadRowsAsync(string, CancellationToken) without ever blocking a request thread on I/O.
Only available on .NET 8 and later; netstandard2.0 has no built-in IAsyncEnumerable<T>.
public sealed class ExcelReader : IDisposable
- Inheritance
-
ExcelReader
- Implements
- Inherited Members
Examples
using var reader = ExcelReader.Open("import.xlsx");
foreach (var row in reader.ReadRows("Products"))
Console.WriteLine($"{row.GetString(0)} = {row.GetDouble(2)}");
var products = reader.Read<Product>("Products").ToList();
Properties
SheetNames
Sheet names in tab order (hidden sheets included).
public IReadOnlyList<string> SheetNames { get; }
Property Value
Methods
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
GetVisibility(string)
Visibility of a sheet, by name.
public SheetVisibility GetVisibility(string sheetName)
Parameters
sheetNamestring
Returns
Open(Stream, bool)
Opens a workbook from a stream. A non-seekable stream (an HTTP upload, say) is first copied into memory, because the zip directory sits at the end of the file.
public static ExcelReader Open(Stream stream, bool leaveOpen = false)
Parameters
Returns
Open(string)
Opens a workbook file for reading. The file is shared for reading so Excel can keep it open.
public static ExcelReader Open(string path)
Parameters
pathstring
Returns
OpenAsync(Stream, bool, CancellationToken)
Opens a workbook from a stream, buffering asynchronously when the stream cannot seek. A seekable stream
(a FileStream or MemoryStream) is wrapped directly, exactly like
Open(Stream, bool). A non-seekable one (an ASP.NET Core request body, an
HttpClient response) must be copied first because the zip directory sits at the end of the
file; the copy uses CopyToAsync-style reads so no thread is blocked on the network, and it
stays in memory up to SheetStream.Reading.ExcelReader.InMemoryBufferLimit (64 MB) before spilling into a temporary file
that is deleted with the reader.
public static Task<ExcelReader> OpenAsync(Stream stream, bool leaveOpen = false, CancellationToken cancellationToken = default)
Parameters
streamStreamThe .xlsx content.
leaveOpenboolTrue to leave
streamopen when the reader is disposed (or, for a non-seekable stream, after it has been copied).cancellationTokenCancellationTokenCancels the copy of a non-seekable stream.
Returns
Remarks
Once the bytes are in place the package directory, workbook, shared strings and styles are parsed synchronously: ZipArchive has no asynchronous API and those parts are small compared with the sheets. The row data itself is read asynchronously by ReadRowsAsync(string, CancellationToken).
OpenAsync(string, CancellationToken)
Opens a workbook file for asynchronous reading. The file is opened for overlapped I/O (Asynchronous) so that ReadRowsAsync(string, CancellationToken) pulls sheet bytes with true asynchronous reads instead of thread-pool emulation. Opening itself is not fully asynchronous: the zip directory, workbook, shared strings and styles are read synchronously because ZipArchive has no asynchronous API; the returned task only surfaces I/O errors (a missing file, say) the way an async caller expects. Like Open(string), the file is shared for reading so Excel can keep it open.
public static Task<ExcelReader> OpenAsync(string path, CancellationToken cancellationToken = default)
Parameters
pathstringcancellationTokenCancellationToken
Returns
ReadAsync<T>(string, ISheetRowMapper<T>, CancellationToken)
Maps rows to objects asynchronously through an explicit ISheetRowMapper<T>, like Read<T>(string, ISheetRowMapper<T>). No reflection is involved, so this is the overload for trimmed and Native AOT applications.
public IAsyncEnumerable<T> ReadAsync<T>(string sheetName, ISheetRowMapper<T> mapper, CancellationToken cancellationToken = default)
Parameters
sheetNamestringmapperISheetRowMapper<T>cancellationTokenCancellationToken
Returns
Type Parameters
T
ReadAsync<T>(string, CancellationToken)
Maps rows to objects asynchronously, with the rules of Read<T>(string): the first row is the header, headers match members case-insensitively, a value that cannot be converted throws a FormatException naming the row and column, and blank rows are skipped. A mapper registered in SheetMappers takes precedence over reflection.
public IAsyncEnumerable<T> ReadAsync<T>(string sheetName, CancellationToken cancellationToken = default) where T : new()
Parameters
sheetNamestringcancellationTokenCancellationToken
Returns
Type Parameters
T
Examples
await foreach (var product in reader.ReadAsync<Product>("Products", ct))
await db.Products.AddAsync(product, ct);
ReadDataTable(string, bool)
Reads a sheet into a DataTable. With firstRowIsHeader the first row
supplies column names (duplicates get a numeric suffix); otherwise columns are named A, B, C...
Column types are object so mixed columns survive.
public DataTable ReadDataTable(string sheetName, bool firstRowIsHeader = true)
Parameters
Returns
ReadDataTableAsync(string, bool, CancellationToken)
Reads a sheet into a DataTable asynchronously, with the column rules of ReadDataTable(string, bool). The table is built in memory, so the gain over the synchronous call is only that the sheet bytes are read without blocking a thread.
public Task<DataTable> ReadDataTableAsync(string sheetName, bool firstRowIsHeader = true, CancellationToken cancellationToken = default)
Parameters
sheetNamestringfirstRowIsHeaderboolcancellationTokenCancellationToken
Returns
ReadHyperlinks(int)
Lists the hyperlinks of a sheet by zero-based position.
public IReadOnlyList<ExcelHyperlink> ReadHyperlinks(int sheetIndex)
Parameters
sheetIndexint
Returns
ReadHyperlinks(string)
Lists the hyperlinks of a sheet by name (case-insensitive). External targets are resolved through the sheet's relationships; in-workbook links surface as Location. The sheet's rows are skipped, not parsed. A sheet without links yields an empty list.
public IReadOnlyList<ExcelHyperlink> ReadHyperlinks(string sheetName)
Parameters
sheetNamestring
Returns
ReadMergedRanges(int)
Lists the merged cell ranges of a sheet by zero-based position.
public IReadOnlyList<MergedRange> ReadMergedRanges(int sheetIndex)
Parameters
sheetIndexint
Returns
ReadMergedRanges(string)
Lists the merged cell ranges of a sheet by name (case-insensitive). The sheet's rows are skipped, not parsed, so this is cheap even on a sheet with a million rows. A sheet without merges yields an empty list.
public IReadOnlyList<MergedRange> ReadMergedRanges(string sheetName)
Parameters
sheetNamestring
Returns
ReadRows(int)
Streams the rows of a sheet by zero-based position.
public IEnumerable<ExcelRow> ReadRows(int sheetIndex)
Parameters
sheetIndexint
Returns
ReadRows(string)
Streams the rows of a sheet by name (case-insensitive).
public IEnumerable<ExcelRow> ReadRows(string sheetName)
Parameters
sheetNamestring
Returns
ReadRowsAsync(int, CancellationToken)
Streams the rows of a sheet by zero-based position; see ReadRowsAsync(string, CancellationToken).
public IAsyncEnumerable<ExcelRow> ReadRowsAsync(int sheetIndex, CancellationToken cancellationToken = default)
Parameters
sheetIndexintcancellationTokenCancellationToken
Returns
ReadRowsAsync(string, CancellationToken)
Streams the rows of a sheet by name (case-insensitive) without blocking a thread: the sheet XML is
parsed with the asynchronous XmlReader methods and cancellation is checked before every
row. Values are identical to ReadRows(string); the two share the cell parsing code.
Breaking out of the await foreach early disposes the XML reader and the zip entry stream.
public IAsyncEnumerable<ExcelRow> ReadRowsAsync(string sheetName, CancellationToken cancellationToken = default)
Parameters
sheetNamestringcancellationTokenCancellationToken
Returns
Examples
using var reader = await ExcelReader.OpenAsync(Request.Body, cancellationToken: ct);
await foreach (var row in reader.ReadRowsAsync("Products", ct))
Console.WriteLine($"{row.GetString(0)} = {row.GetDouble(2)}");
Read<T>(string)
Maps rows to objects. The first row is the header; each header is matched (case-insensitively) to a writable property or field by SheetColumnAttribute name, [DisplayName] or member name. Values are converted to the member type; a value that cannot be converted throws a FormatException naming the row and column. Unmatched columns are ignored.
public IEnumerable<T> Read<T>(string sheetName) where T : new()
Parameters
sheetNamestring
Returns
- IEnumerable<T>
Type Parameters
T
Read<T>(string, ISheetRowMapper<T>)
Maps rows to objects through an explicit ISheetRowMapper<T> (for example
Order.SheetMapper generated for a [SheetRow] type). Behaves like
Read<T>(string): the first row is the header, headers are matched case-insensitively,
values are converted to the member type and a failed conversion throws a FormatException
naming the row and column. No reflection is used, so this overload is safe for trimmed and
Native AOT applications.
public IEnumerable<T> Read<T>(string sheetName, ISheetRowMapper<T> mapper)
Parameters
sheetNamestringmapperISheetRowMapper<T>
Returns
- IEnumerable<T>
Type Parameters
T