v3.0.0
⚠️ Breaking changes
-
IExcelRecordMap<T>.ConfigureExcelRecordMapis now generic in the row writer.
Old:static abstract void ConfigureExcelRecordMap(ExcelRecordMapBuilder<T> builder);
New:static abstract void ConfigureExcelRecordMap<TRow>(ExcelRecordMapBuilder<T, TRow> builder) where TRow : IRowWriter;This lets the mapped record-writer path compile each column's write action against the concrete row writer (
XlsxRowWriter,XlsbRowWriter, ...) instead of dispatching through theIRowWriterinterface on every cell — the same optimization the reflection-based writer already had via its per-TRowplan.Who's affected: only hand-written
IExcelRecordMap<T>implementations. If your types use[ExcelSerializable](the source generator), nothing to do — just recompile. A hand-written implementation needs updating:// before public static void ConfigureExcelRecordMap(ExcelRecordMapBuilder<MyRecord> builder) { ... } // after public static void ConfigureExcelRecordMap<TRow>(ExcelRecordMapBuilder<MyRecord, TRow> builder) where TRow : IRowWriter { ... }
New: encrypted OOXML writing
Excel.EncryptPackage/EncryptPackageAsync: encrypt an OOXML package into an agile-encrypted CFB container (file and stream overloads). Pairs with the encryption reading support shipped in v2.3.0 — round-tripping a password-protected workbook no longer needs a third-party tool.- Underlying pieces now public/available: agile
EncryptionInfodescriptor serialization, the write direction of agile key derivation, and an OLE compound writer generalized to N streams with mini-FAT support.
New: ADO.NET access
ExcelDataReaderimplementsIDataReader, giving Excel/CSV data a standardIDataReadersurface (GetValue,GetSchemaTable,GetOrdinal, typedGetXxxaccessors, etc.) for code that expects one — DataTables, data-binding, existing ADO.NET pipelines.ExcelRowReaderExtensions.Sheets()/ExcelSheet: enumerate a workbook's sheets (index + name) without opening each one.
New: native bindings parity
- Python:
SheetWriter, a streaming write handle —write_rowwith type inference, in-memory streaming, and columnar writing. Encrypted writing wired into the native bindings. - Rust:
Workbook::rows, a zero-allocation lending row cursor, andWorkbook::read_all_decodedfor an RAII row set. - C++: the same
Workbook::rowsbuffer-reusing cursor andread_all_decodedcounterparts. ExcelReader.Arrow:RecordBatchwrite support (previously read-only).
Fixes
- XLSB integers are ~2x smaller. Numeric cells with an integral value in
[-2^29, 2^29)now encode as the 4-byteBrtCellRk(fInt) form instead of always paying for the 8-byteBrtCellReal— matching what Excel itself emits for a sheet of integers. XlsbRowWriter.Write<T>overflow errors now name the type. A caller-definedIUtf8SpanFormattablethat overflows to±Infinityduring numeric conversion previously surfaced a bare "non-finite value" error; it now names the source type, matching the message you already got for a non-numeric value.- Corrupt mini-stream sectors fail loudly.
ReadMiniStreamnow throwsInvalidDataExceptionfor an out-of-bounds mini-sector slice instead of letting the read overflow silently. - Fixed a
NativeAOTstartup crash affecting the Python wheel and C/C++/Rust bindings. The native library no longer pinsIlcInstructionSet=native. That setting baked the build machine's CPU features into the binary as a hard requirement; because the binary is built on one machine and consumed on another (CI runner, or an end user's machine after installing the wheel), a CPU-feature mismatch made the process abort at startup with "The required instruction sets are not supported by the current CPU." — intermittently in CI, and potentially for any user whose CPU lacked whatever the build host happened to have (e.g. AVX-512). Vectorized code paths still light up at runtime on capable CPUs viaVector128/256.IsHardwareAcceleratedchecks, which are unaffected by this change. - Data integrity checks strengthened; mapped-record-writer benchmarks added for the new
ExcelRecordMapBuilder<T, TRow>path. - Dependency bumps:
dorny/paths-filter3→4, analyzer packages.
Compatibility
- Reading is unaffected — no breaking changes to any parser, reader, or the generated
IExcelRowMap<T>side. - .NET 8 and .NET 10 both supported, as before.