Excel-PRIME 4.2605.17

dotnet add package Excel-PRIME --version 4.2605.17
                    
NuGet\Install-Package Excel-PRIME -Version 4.2605.17
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Excel-PRIME" Version="4.2605.17" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Excel-PRIME" Version="4.2605.17" />
                    
Directory.Packages.props
<PackageReference Include="Excel-PRIME" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Excel-PRIME --version 4.2605.17
                    
#r "nuget: Excel-PRIME, 4.2605.17"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Excel-PRIME@4.2605.17
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Excel-PRIME&version=4.2605.17
                    
Install as a Cake Addin
#tool nuget:?package=Excel-PRIME&version=4.2605.17
                    
Install as a Cake Tool

Excel_PRIME 🌟

  • Excel_Performant Reader via Interfaces for Memory Efficiency.
  • Without using any external libraries.
  • Optimised for Range extraction.

What does that mean?

  • Yet another Excel reader ?,
    • Starting with .Net 8 as the performant Runtime (See Benchmarks)
    • .Net9 gives an extra 5% boost,
    • .Net10 Another 5% over .Net9 😉

Lets take each of the above elements and explain:

Excel 📈

  • Open Large 2007 (Onwards) XLSX file formats and XLSB in V3.##
  • Zip Deflate format Only

Performant 🚀

  • Try to be as fast as possible, i.e.
    • Forward only Lazy loading
    • Only "Quick" decipher / convert of the cell(s) types to ease GC pressure
    • No attempt at "creating / using" datatables with headers etc.
    • Use IEnumerables with initial offset starts (Row / Column)
    • Allow CancellationTokens to be used to allow page transitioning cancellation (More on this later)
  • Now the fastest in Real world usage 2025-11-19 onwards
  • Strongly-typed accessors (AsInt32, AsDateTime, etc)

Q & A's

  • Q: There are others that are faster
  • A: Agreed, but then
    • They do not have range extraction.
    • Or optionally allow the use of the OS's TempFile System to store massive sheets
    • Or re-use of already extracted (massive) sheets
    • Or allow multiple sheets to be read at the same time
      • because others use global memory to represent the current row
      • Or have a single access into the Zip Excel file

Reader 📋

  • Read only
    • Therefore no calculations or updates to formula calls

Interfaces 🏗️

  • Will use the DotNet core functionality by default
  • But, if your target deployment allows for the use of native performant binaries, then via the use of interfaces these will be pluggable
    • i.e. Using Zlib.Net for getting the data streams out of the compressed Excel file faster. (Or SharpZipLib / PowerPlayZipper)
    • A faster / slimmer implementation for xml stream reading (i.e. TurboXml)
  • Allow the implementation of different source files (i.e. XLSB)

Q & A's

  • Q: Why?
  • A: As mentioned above, this is to allow a developer to replace with external nugets that might perform better XML speed etc.

Memory 🌐

  • The reason for this project, is to handle very large XSLX files (i.e. > 500K rows with > 180 columns per sheet, with multiple sheets of this size)
  • For ETL validation scenarios, i.e. make sure that the user modified data that has been transferred has interaction rules applied, before moving onto the T and L stages
  • Try not to hit / store in the LOH
  • No internal .Net memory of previously loaded sheets / rows.

Q & A's

  • Q: It appears that this uses more memory than other implementations
  • A: Currently yes, but it is being optimised for Range Extraction,
    • AND for allowing multiple rows (With cell data) to be stored in memory at the same time, (i.e. via ToList() call);
    • AND to allow multiple sheets to be read at the same time (Unlike some to of the others that use "a single" global memory to represent a row)
    • And it appears that the current benchmarks do not extract unless a ToString and a check on the result is used (Otherwise the Jit removes the unassigned dead code)
    • And, the memory used will actually be used in the ETL pipeline anyway, so it's just being truthful

Efficiency 📦

  • As hinted by the above statements, this is to be targetted at memory restricted environments (i.e. ASP Net VM's)
  • Use the OS's Temp File caching, so if the memory is tight then the Owner app will not have to worry about OOM exceptions, or having to use Swap Disk speeds.
  • Only unzip the sheet(s) when they are asked for
  • Only load the shared strings upto the current request number

Q & A's

  • Q: Sometimes the Async await s add too much overhead
  • A: true, that is why there are also the equivalent base interfaces that perform the same functionality without the need for the async await overheads, and still with ConcellationToken usage(s).

Etc. 🔧

CancellationTokens

  • This is to allow the Large files to be Aborted
  • Make "Most" of the "Net Cores'" API's Asynchronous Tasks

IDisposable

  • Got to tidy up those Temp Files, and release the FileStream's

Caveats ⛔:

Not Be: Same sheet Thread safe 📊

  • It will Not be same sheet Instance thread safe, because the xml reader will be locked (Forward only) to the sheet in use.
    • but you can Open the sheet more than once, and have different threads running over it,
    • And you can have Parallel threads access the Excel file
    • Just remember to set Options{ AccessExcelFileInForwardOnlyMode = false}

Not Do: Dynamic Ranges ⚠️

  • i.e. Ones that contain formulas:
    • <definedName name="Prices">OFFSET(Sheet1!$A$1,0,0,COUNTA(Sheet1!$A:$A),1)</definedName>

Not Do: Poco 🤖

  • A POCO / Type populator (Extensions can be written for that later)

Not Be a: Writer / Modifier 📚

  • Totally beyond the scope of this project remit

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

# 2026-05-12 - V4 - Bug fixes
- Return null when `EndValue` is potted in the xml #22
- Return null for not found SheetId #23
- Changed CellValue to an abstract base class and removed all [FieldOffset(0)] fields.
- Introduced internal sealed class CellValue<T> : CellValue to store values of type T without boxing for value types.

# 2026-05-05 - V4
- ⛓️‍💥 **Breaking Change(s)**
   - Removal of `GetSheetFileName(int offsetSheetId);`
   - Removal of `GetDefinedRange` via `int sheetId`
   - Removal of `Index` property from `ISheet`
   - Internal Creation of WorkBooks
   - Internal implementation of `IOpenXmlWorkBookReader::GetSheetNames` now returns the relative path to the sheetName
   - `CellValue` is now a `class`, therefore no need to use `.Value`
   - `ICell.CellValue` is now nullable
- `ArrayPool` support has been added to ThreadStringBuilderPool using ArrayPool<char>.
- Release-specific optimizations added
 - EnableTrimAnalyzer: true
 - TieredCompilation: true
 - TieredCompilationQuickJit: true
 - TieredCompilationQuickJitForLoops: true
- Implement `System.DBNull` return option, for empty cells
 - Implement `INullRow` return option, for empty rows
 - Update tests to use `INullRow` detection
- Implement `GetCell###(string columnLetters, ...)` #8
- Implement `System.DBNull` return option, for empty cells
- Mark up usage of userdefined cells styles for V5
- Remove `in` usages (Supposed to not benefit !)
- Make use of ThreadStatic in XlsbRow
- Remove secoundary usage of a struct
- Re-introduce the CellConversion for the XLSX cell types
- Use `ValueTask` and reduce memory allocations in some hot paths
- Cell object type 📅
   - "Best Effort" `Operator` based conversion
   - TryGet`Type` will return `out type`, if stored as that type.
- Add `Ecma376StandardProvider`
- Add `StylesExtractor`
- Attempt to make use of the Cell types
- Tinker with some `MethodImpl`
- Add `_iStyleRef` and start to add formatting based on it
- Fix fallout from making `CellValue` is now a `class`
- 🚀 [2026-05-05](https://github.com/Smurf-IV/Excel_PRIME/blob/main/Performance.md#2026-05-05---v4)