Class WorkbookEditor
- Namespace
- DocToolkit
- Assembly
- DocToolkit.dll
Creates, reads and edits Excel (.xlsx) workbooks. Legacy .xls is not supported.
public static class WorkbookEditor
- Inheritance
-
WorkbookEditor
- Inherited Members
Methods
AppendRows(byte[], string, IEnumerable<IEnumerable<object?>>)
Appends rows to sheetName, starting immediately after
its last used row, and returns the updated workbook. Every other sheet, and all existing
formatting, is left as it was.
"Last used row" comes from ClosedXML's LastRowUsed(), which — like
LastCellUsed() in ReadSheet(byte[], string) — ignores formatting but counts a cell
comment as used even with no value. A comment on an otherwise-blank row far below the real
data therefore pushes the append down to start after that row, leaving a gap rather than
continuing immediately after the last row a caller would see as holding data.
Cell typing and culture rules are identical to
Create(string, IEnumerable<IEnumerable<object?>>). A cell holding an
XlsxFormula is written as a formula. An empty rows is a
no-op that still returns a valid workbook.
public static byte[] AppendRows(byte[] xlsx, string sheetName, IEnumerable<IEnumerable<object?>> rows)
Parameters
xlsxbyte[]sheetNamestringrowsIEnumerable<IEnumerable<object>>
Returns
- byte[]
Exceptions
- ArgumentNullException
xlsxorrowsis null.- ArgumentException
xlsxis empty;sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; or an element ofrowsis null.- DocumentConversionException
The sheet was not found, or the package could not be opened or edited.
AppendRowsAsync(Stream, string, IEnumerable<IEnumerable<object?>>, Stream, CancellationToken)
Reads a workbook from source, appends rows to
sheetName, and writes the result to destination. See
AppendRows(byte[], string, IEnumerable<IEnumerable<object?>>) for the
semantics, including exactly what "last used row" means.
source is read to its end and destination
is written; neither is disposed, closed or sought.
public static Task AppendRowsAsync(Stream source, string sheetName, IEnumerable<IEnumerable<object?>> rows, Stream destination, CancellationToken ct = default)
Parameters
sourceStreamThe stream the workbook is read from.
sheetNamestringThe sheet to append to.
rowsIEnumerable<IEnumerable<object>>The rows to append.
destinationStreamThe stream the updated workbook is written to.
ctCancellationTokenCancels the read, the edit and the write.
Returns
Exceptions
- ArgumentNullException
rowsis null.- ArgumentException
sourceis not readable or held no bytes;destinationis not writable;sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; or an element ofrowsis null.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The sheet was not found, or the package could not be opened or edited.
AppendRowsAsync(string, string, string, IEnumerable<IEnumerable<object?>>, CancellationToken)
Reads a workbook from inputPath, appends rows to
sheetName, and writes the result to outputPath,
overwriting any existing file. See
AppendRows(byte[], string, IEnumerable<IEnumerable<object?>>) for the
semantics, including exactly what "last used row" means.
public static Task AppendRowsAsync(string inputPath, string outputPath, string sheetName, IEnumerable<IEnumerable<object?>> rows, CancellationToken ct = default)
Parameters
inputPathstringThe workbook to read.
outputPathstringWhere to write the result. Overwritten if it exists.
sheetNamestringThe sheet to append to.
rowsIEnumerable<IEnumerable<object>>The rows to append.
ctCancellationTokenCancels the read and the write.
Returns
Exceptions
- ArgumentNullException
rowsis null.- ArgumentException
inputPathoroutputPathis blank;sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; or an element ofrowsis null.- FileNotFoundException
inputPathdoes not exist.- DirectoryNotFoundException
inputPath's oroutputPath's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The sheet was not found, or the package could not be opened or edited.
Create(IEnumerable<XlsxSheet>)
Builds a workbook from sheets, one worksheet each, in sequence order.
Content comes from data rather than a template, so there is no source file to edit.
Cell typing and culture rules are identical to Create(string, IEnumerable<IEnumerable<object?>>). A cell holding an XlsxFormula is written as a formula — see that type for the one limit worth knowing about cached values.
public static byte[] Create(IEnumerable<XlsxSheet> sheets)
Parameters
sheetsIEnumerable<XlsxSheet>
Returns
- byte[]
Examples
byte[] xlsx = WorkbookEditor.Create(new[]
{
XlsxSheet.Named("Sales", new[]
{
new object?[] { "Region", "Q1" },
new object?[] { "North", 1200 },
new object?[] { "Total", XlsxFormula.From("=SUM(B2:B2)") },
}),
});
Exceptions
- ArgumentNullException
sheetsis null.- ArgumentException
sheetsis empty, contains a null element, or names the same sheet twice.- DocumentConversionException
The workbook could not be built.
Create(string, IEnumerable<IEnumerable<object?>>)
Creates a workbook with one sheet populated from rows.
Every built-in numeric type is written as a number so formulas such as SUM() pick it up; DateTime and DateOnly become dates, TimeOnly and TimeSpan become durations. Anything else is formatted with InvariantCulture, so the same code produces the same spreadsheet on every machine.
public static byte[] Create(string sheetName, IEnumerable<IEnumerable<object?>> rows)
Parameters
sheetNamestringrowsIEnumerable<IEnumerable<object>>
Returns
- byte[]
Exceptions
- ArgumentNullException
rowsis null.- ArgumentException
sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; or a row is null.- DocumentConversionException
The workbook could not be built.
CreateAsync(IEnumerable<XlsxSheet>, Stream, CancellationToken)
Builds a workbook from sheets and writes it to
destination. See Create(IEnumerable<XlsxSheet>) for the
semantics — this overload applies identical logic.
destination is written, from its current position, and is
not disposed, closed or sought.
public static Task CreateAsync(IEnumerable<XlsxSheet> sheets, Stream destination, CancellationToken ct = default)
Parameters
sheetsIEnumerable<XlsxSheet>The sheets to build the workbook from, one worksheet each.
destinationStreamThe stream the workbook is written to.
ctCancellationTokenCancels the build and the write to
destination.
Returns
Exceptions
- ArgumentNullException
sheetsordestinationis null.- ArgumentException
sheetsis empty, contains a null element, or names the same sheet twice; ordestinationis not writable.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be built or written.
CreateAsync(string, IEnumerable<IEnumerable<object?>>, Stream, CancellationToken)
Builds a workbook with one sheet populated from rows and writes it to
destination. See Create(string, IEnumerable<IEnumerable<object?>>)
for the exact typing and culture rules applied to each cell — this overload applies the
identical logic, writing to destination instead of returning an array.
destination is written, from its current position, and is
not disposed, closed or sought — it belongs to the caller, and may be write-only and
forward-only, such as an HTTP response body.
public static Task CreateAsync(string sheetName, IEnumerable<IEnumerable<object?>> rows, Stream destination, CancellationToken ct = default)
Parameters
sheetNamestringThe name of the sheet to create.
rowsIEnumerable<IEnumerable<object>>The rows to populate it with.
destinationStreamThe stream the workbook is written to.
ctCancellationTokenCancels the build and the write to
destination.
Returns
Exceptions
- ArgumentNullException
rowsordestinationis null.- ArgumentException
sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; a row is null; ordestinationis not writable.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be built or written.
CreateToFileAsync(IEnumerable<XlsxSheet>, string, CancellationToken)
Builds a workbook from sheets and writes it to
outputPath, overwriting any existing file. See
Create(IEnumerable<XlsxSheet>) for the semantics.
public static Task CreateToFileAsync(IEnumerable<XlsxSheet> sheets, string outputPath, CancellationToken ct = default)
Parameters
sheetsIEnumerable<XlsxSheet>The sheets to build the workbook from, one worksheet each.
outputPathstringWhere to write the workbook. Overwritten if it exists.
ctCancellationTokenCancels the write to
outputPath.
Returns
Exceptions
- ArgumentNullException
sheetsoroutputPathis null.- ArgumentException
sheetsis empty, contains a null element, or names the same sheet twice; oroutputPathis blank.- DirectoryNotFoundException
outputPath's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be built or written.
CreateToFileAsync(string, IEnumerable<IEnumerable<object?>>, string, CancellationToken)
Builds a workbook with one sheet populated from rows and writes it to
outputPath. See Create(string, IEnumerable<IEnumerable<object?>>)
for the exact typing and culture rules applied to each cell — this overload applies the
identical logic, writing to outputPath instead of returning an array.
Named CreateToFileAsync rather than a third CreateAsync overload: sheetName and rows come first, same as
CreateAsync(string, IEnumerable<IEnumerable<object?>>, Stream, CancellationToken),
but the destination is a string path instead of a Stream — the distinct name
keeps which kind of destination a call writes to visible at the call site, rather than
resting on the argument type alone.
public static Task CreateToFileAsync(string sheetName, IEnumerable<IEnumerable<object?>> rows, string outputPath, CancellationToken ct = default)
Parameters
sheetNamestringThe name of the sheet to create.
rowsIEnumerable<IEnumerable<object>>The rows to populate it with.
outputPathstringWhere to write the workbook. Overwritten if it exists.
ctCancellationTokenCancels the write to
outputPath.
Returns
Exceptions
- ArgumentNullException
outputPath,sheetNameorrowsis null.- ArgumentException
outputPathis blank;sheetNameis blank, is longer than 31 characters, or contains one of: \ / ? * [ ]; or a row is null.- DirectoryNotFoundException
outputPath's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be built.
ReadCell(byte[], string, string)
Reads a cell as a string. cellRef is an A1-style reference. Text follows
the calling thread's CurrentCulture — see
ReadSheet(byte[], string) for the full rule.
public static string ReadCell(byte[] xlsx, string sheetName, string cellRef)
Parameters
Returns
Exceptions
- ArgumentNullException
Any argument is null.
- ArgumentException
xlsxis empty, or a name is blank.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
ReadCellAsync(Stream, string, string, CancellationToken)
Reads a workbook from source and returns a cell as a string.
cellRef is an A1-style reference. source is
read to its end and is neither disposed, closed nor sought.
public static Task<string> ReadCellAsync(Stream source, string sheetName, string cellRef, CancellationToken ct = default)
Parameters
sourceStreamsheetNamestringcellRefstringctCancellationToken
Returns
Exceptions
- ArgumentNullException
sourceis null.- ArgumentException
sourceis not readable or held no bytes, or a name is blank.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
ReadCellAsync(string, string, string, CancellationToken)
Reads a workbook from path and returns a cell as a string.
cellRef is an A1-style reference. See ReadCell(byte[], string, string) for the
culture rule applied to the text.
public static Task<string> ReadCellAsync(string path, string sheetName, string cellRef, CancellationToken ct = default)
Parameters
pathstringThe workbook to read.
sheetNamestringThe sheet containing the cell.
cellRefstringAn A1-style cell reference, e.g.
"B2".ctCancellationTokenCancels the read.
Returns
Exceptions
- ArgumentNullException
A path or a name is null.
- ArgumentException
pathor a name is blank, or the file it names is empty.- FileNotFoundException
pathdoes not exist.- DirectoryNotFoundException
path's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
ReadSheet(byte[], string)
Reads a whole sheet as strings, anchored at A1: if the data starts at C3, its first value
is at rows[2][2]. Every row is padded to the last used column, so all rows have the
same length; blank cells — and entirely blank rows inside the range — come back as empty
strings rather than being dropped, which keeps rows[r][c] positionally meaningful.
Values are produced exactly as ReadCell(byte[], string, string) produces them, so the two can never disagree about what a cell says. A formula cell reads back one of two ways, and which one depends on the file rather than on this library:
- If the file carries a cached value, as one Excel has saved does, that cached value is returned and the formula is not evaluated. It can therefore be stale — a workbook whose inputs were edited by something that did not recalculate reports the old result.
-
If it does not — which is what this library writes, see XlsxFormula —
ClosedXML evaluates the formula on read, so a cell holding
=A1+A2over 1 and 2 reads back as"3", and one that cannot be evaluated reads back as its Excel error string (#DIV/0!,#NAME?,#REF!) rather than throwing.
Text follows the calling thread's CurrentCulture
— the same rule ReadCell(byte[], string, string) uses, and asymmetric with
Create(string, IEnumerable<IEnumerable<object?>>), which
deliberately writes with InvariantCulture so
the same code produces the same file everywhere. A number such as 1234.5 reads back
as "1234.5" under an invariant or en-US culture but "1234,5" under de-DE;
callers who parse the returned text as a number should account for that, e.g. by parsing
with an explicit CultureInfo rather than the default.
public static IReadOnlyList<IReadOnlyList<string>> ReadSheet(byte[] xlsx, string sheetName)
Parameters
Returns
- IReadOnlyList<IReadOnlyList<string>>
The sheet's used range, anchored at A1 and padded rectangular. Empty only if the sheet holds no values and no cell comments: formatting alone never widens the range, but a comment on an otherwise-blank cell does, because ClosedXML's
LastCellUsed()counts it as used.
Examples
// No need to know the workbook's shape in advance.
foreach (string name in WorkbookEditor.SheetNames(xlsx))
{
IReadOnlyList<IReadOnlyList<string>> grid = WorkbookEditor.ReadSheet(xlsx, name);
Console.WriteLine($"{name}: {grid.Count} rows x {grid[0].Count} columns");
}
Remarks
The whole range is materialised into memory at once, so its cost is proportional to
rows × columns, not to how much of that rectangle actually holds data. To keep
one far-flung stray value from exhausting memory, ReadSheet(byte[], string) throws
DocumentConversionException rather than allocate when the used range exceeds
2,000,000 cells.
Exceptions
- ArgumentNullException
Any argument is null.
- ArgumentException
xlsxis empty, orsheetNameis blank.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the sheet's used range exceeds the 2,000,000-cell limit ReadSheet(byte[], string) will materialise.
ReadSheetAsync(Stream, string, CancellationToken)
Reads a workbook from source and returns a whole sheet as strings. See
ReadSheet(byte[], string) for the anchoring, padding, culture and formula rules — this
overload applies the identical logic. source is read to its end
and is neither disposed, closed nor sought.
public static Task<IReadOnlyList<IReadOnlyList<string>>> ReadSheetAsync(Stream source, string sheetName, CancellationToken ct = default)
Parameters
sourceStreamThe stream the workbook is read from.
sheetNamestringThe sheet to read.
ctCancellationTokenCancels the read.
Returns
- Task<IReadOnlyList<IReadOnlyList<string>>>
The sheet's used range, anchored at A1 and padded rectangular. Empty only if the sheet holds no values and no cell comments — see ReadSheet(byte[], string).
Exceptions
- ArgumentNullException
sourceis null.- ArgumentException
sourceis not readable or held no bytes, orsheetNameis blank.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the sheet's used range exceeds the 2,000,000-cell limit ReadSheet(byte[], string) will materialise.
ReadSheetAsync(string, string, CancellationToken)
Reads a workbook from path and returns a whole sheet as strings. See
ReadSheet(byte[], string) for the anchoring, padding, culture and formula rules — this
overload applies the identical logic.
public static Task<IReadOnlyList<IReadOnlyList<string>>> ReadSheetAsync(string path, string sheetName, CancellationToken ct = default)
Parameters
pathstringThe workbook to read.
sheetNamestringThe sheet to read.
ctCancellationTokenCancels the read.
Returns
- Task<IReadOnlyList<IReadOnlyList<string>>>
The sheet's used range, anchored at A1 and padded rectangular. Empty only if the sheet holds no values and no cell comments — see ReadSheet(byte[], string).
Exceptions
- ArgumentNullException
A path or
sheetNameis null.- ArgumentException
pathorsheetNameis blank, or the file atpathis empty.- FileNotFoundException
pathdoes not exist.- DirectoryNotFoundException
path's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the sheet's used range exceeds the 2,000,000-cell limit ReadSheet(byte[], string) will materialise.
SetCell(byte[], string, string, object?)
Sets a cell and returns the updated workbook bytes. A cell holding an XlsxFormula is written as a formula instead of a literal value — see that type for the one limit worth knowing about cached values.
public static byte[] SetCell(byte[] xlsx, string sheetName, string cellRef, object? value)
Parameters
Returns
- byte[]
Exceptions
- ArgumentNullException
Any argument other than
valueis null.- ArgumentException
xlsxis empty, or a name is blank.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
SetCellAsync(Stream, string, string, object?, Stream, CancellationToken)
Reads a workbook from source, sets one cell, and writes the result to
destination. cellRef is an A1-style reference. A cell
holding an XlsxFormula is written as a formula instead of a literal value —
see that type for the one limit worth knowing about cached values.
source is read to its end and destination is
written; neither is disposed, closed or sought, and neither has to be seekable.
public static Task SetCellAsync(Stream source, string sheetName, string cellRef, object? value, Stream destination, CancellationToken ct = default)
Parameters
sourceStreamThe stream the workbook is read from.
sheetNamestringThe sheet containing the cell.
cellRefstringAn A1-style cell reference, e.g.
"B2".valueobjectThe value to write.
nullclears the cell; an XlsxFormula writes a formula.destinationStreamThe stream the updated workbook is written to.
ctCancellationTokenCancels the read, the edit and the write.
Returns
Exceptions
- ArgumentNullException
sourceordestinationis null.- ArgumentException
sourceis not readable or held no bytes, a name is blank, ordestinationis not writable.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
SetCellAsync(string, string, string, string, object?, CancellationToken)
Reads a workbook from inputPath, sets one cell, and writes the result to
outputPath. cellRef is an A1-style reference. A cell
holding an XlsxFormula is written as a formula instead of a literal value —
see that type for the one limit worth knowing about cached values. The two paths may be
the same file: the updated bytes are computed in full before
outputPath is opened, so a workbook that fails to process — cannot be
read, or cannot be edited — leaves outputPath untouched. That guarantee
does not extend to a failure during the write itself: a full disk, a cancellation, or the
process dying mid-write can still leave a partial file, so in-place editing of an
irreplaceable workbook is not crash-safe.
public static Task SetCellAsync(string inputPath, string outputPath, string sheetName, string cellRef, object? value, CancellationToken ct = default)
Parameters
inputPathstringThe workbook to read.
outputPathstringWhere to write the result. Overwritten if it exists.
sheetNamestringThe sheet containing the cell.
cellRefstringAn A1-style cell reference, e.g.
"B2".valueobjectThe value to write.
nullclears the cell; an XlsxFormula writes a formula.ctCancellationTokenCancels the read and the write.
Returns
Exceptions
- ArgumentNullException
A path or a name is null.
- ArgumentException
A path or a name is blank, or the file at
inputPathis empty.- FileNotFoundException
inputPathdoes not exist.- DirectoryNotFoundException
inputPath's oroutputPath's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened, the sheet does not exist, or the reference is not valid.
SheetNames(byte[])
Lists every sheet in the workbook, in tab order, including hidden sheets — hiding a sheet is a presentation choice, not a privacy boundary, and a caller who cannot see a hidden sheet listed has no way to discover it exists.
public static IReadOnlyList<string> SheetNames(byte[] xlsx)
Parameters
xlsxbyte[]The workbook bytes.
Returns
- IReadOnlyList<string>
The sheet names, in tab order.
Exceptions
- ArgumentNullException
xlsxis null.- ArgumentException
xlsxis empty.- DocumentConversionException
The workbook could not be opened.
SheetNamesAsync(Stream, CancellationToken)
Reads a workbook from source and lists every sheet in tab order,
including hidden sheets. source is read to its end and is neither
disposed, closed nor sought.
public static Task<IReadOnlyList<string>> SheetNamesAsync(Stream source, CancellationToken ct = default)
Parameters
sourceStreamThe stream the workbook is read from.
ctCancellationTokenCancels the read.
Returns
- Task<IReadOnlyList<string>>
The sheet names, in tab order.
Exceptions
- ArgumentNullException
sourceis null.- ArgumentException
sourceis not readable or held no bytes.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened.
SheetNamesAsync(string, CancellationToken)
Reads a workbook from path and lists every sheet in tab order, including
hidden sheets. See SheetNames(byte[]) for the full rule.
public static Task<IReadOnlyList<string>> SheetNamesAsync(string path, CancellationToken ct = default)
Parameters
pathstringThe workbook to read.
ctCancellationTokenCancels the read.
Returns
- Task<IReadOnlyList<string>>
The sheet names, in tab order.
Exceptions
- ArgumentNullException
pathis null.- ArgumentException
pathis blank, or the file it names is empty.- FileNotFoundException
pathdoes not exist.- DirectoryNotFoundException
path's directory does not exist.- OperationCanceledException
ctwas cancelled.- DocumentConversionException
The workbook could not be opened.