ExcelFlow 1.2.0

dotnet add package ExcelFlow --version 1.2.0
                    
NuGet\Install-Package ExcelFlow -Version 1.2.0
                    
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="ExcelFlow" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ExcelFlow" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="ExcelFlow" />
                    
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 ExcelFlow --version 1.2.0
                    
#r "nuget: ExcelFlow, 1.2.0"
                    
#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 ExcelFlow@1.2.0
                    
#: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=ExcelFlow&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=ExcelFlow&version=1.2.0
                    
Install as a Cake Tool

ExcelFlow

NuGet Version .NET Native AOT Ready License: MIT

ExcelFlow is a high-performance, strictly typed, and memory-efficient Excel (.xlsx) reader and writer designed specifically for modern .NET.

Built from the ground up to support Native AOT and Assembly Trimming, ExcelFlow eliminates runtime reflection entirely by leveraging Roslyn Source Generators. It uses forward-only streaming to process hundreds of thousands of rows with a minimal memory footprint.

🚀 Key Features

  • Native AOT & Zero Reflection: Mappers are fully generated at compile time as strictly typed code. No reflection, no runtime code generation, no surprises after dotnet publish /p:PublishAot=true.
  • Compile-Time Diagnostics: Mistakes surface as build errors/warnings (EXF001–EXF003), not as silent runtime failures.
  • High Performance & Low Memory: Strict forward-only streaming for both reading and writing. Rows are processed one at a time — the dataset is never fully materialized unless you ask for a List<T>.
  • Fluent Data Validation: Define business rules inline. Invalid rows are filtered out automatically.
  • Graceful Error Handling: Catch parsing and type-conversion errors via .OnError() callbacks with exact 1-based sheet row numbers, instead of dealing with random runtime exceptions.
  • Skip Rows & Layout Resilience: Easily skip title rows or descriptive headers before parsing the actual table structure.
  • Proper Date Cells: Exported DateTime values are written with a real date format — Excel shows dates, not raw numbers.
  • Asynchronous Streaming: First-class support for IAsyncEnumerable<T> (ideal for web APIs and microservices).

📦 Installation

dotnet add package ExcelFlow

The package ships the source generator as a Roslyn analyzer — no extra packages needed.

📖 Quick Start

1. Define your model

Mark your class with [ExcelFlowSerializable] to trigger the Source Generator. Map properties to Excel columns using [ExcelColumn]. You can map columns by Name, by Index, and define whether they are required.

using ExcelFlow;

namespace MyApp;

[ExcelFlowSerializable]
public partial class SalesRecord
{
    [ExcelColumn("Transaction ID")]
    public int TransactionId { get; set; }

    [ExcelColumn("Product Name", IsRequired = true)] // Fail-fast if missing
    public string? ProductName { get; set; }

    [ExcelColumn(Index = 2)] // Direct mapping by column index (0-based)
    public decimal Amount { get; set; }

    [ExcelColumn("Date")]
    public DateTime Date { get; set; }

    [ExcelColumn("Status")]
    public OrderStatus Status { get; set; } // enums are supported

    [ExcelColumn("External ID")]
    public Guid? ExternalId { get; set; }   // so are Guid and nullable types
}

Note: The class must be partial to allow the Source Generator to extend it. If you forget, the build fails with a clear EXF001 error.

Natively supported property types: string, bool, all numeric primitives, DateTime, Guid, enums — plus their nullable variants. Anything else produces an EXF002 build warning and falls back to Convert.ChangeType at runtime.

2. Reading Data

Read the data synchronously into a List<T>, or stream it asynchronously with rich validation and error handling.

using ExcelFlow;

var records = await Excel.Read<SalesRecord>("sales_data.xlsx")
    .FromSheet("Q1_Sales")
    .SkipRows(2) // Skip title and description rows before the header
    .Validate(row => row.Amount >= 0, "Amount cannot be negative")
    .OnError(error =>
    {
        // RowNumber is the real 1-based sheet row — open the file and jump straight to it
        Console.WriteLine($"[Warning] Row {error.RowNumber}: Column '{error.ColumnName}' failed. " +
                          $"Expected {error.ExpectedType}, but got '{error.RawValue}'.");
    })
    .ToListAsync(); // Or use .AsAsyncEnumerable() for true streaming

.OnError() can be called multiple times — all handlers are invoked.

3. Writing Data

Export collections to Excel with compile-time generated writers. DateTime cells get a proper date format automatically (date-only values render as dates, timestamps as date + time).

using ExcelFlow;

List<SalesRecord> data = GetSalesData();

Excel.Write(data)
     .ToSheet("Exported_Sales")
     .ToFile("report.xlsx");

🛠 Advanced: Async Streaming

If you are building a memory-constrained worker service or web endpoint, you can process rows one-by-one as they are parsed, without materializing the whole dataset:

await using var fileStream = File.OpenRead("huge_dataset.xlsx");

var asyncStream = Excel.Read<SalesRecord>(fileStream)
    .AsAsyncEnumerable(cancellationToken);

await foreach (var record in asyncStream)
{
    await _database.InsertAsync(record);
}

Memory note: rows are streamed, but the xlsx shared-string table is loaded once per file (a property of the format itself). For files with millions of unique strings, expect the baseline memory to be proportional to the shared-string table, not to the row count.

Stream ownership

Since v1.2.0 ExcelFlow does not dispose your stream — you own it (this matches .NET conventions, e.g. IFormFile.OpenReadStream() in ASP.NET Core). If you want ExcelFlow to dispose the stream when reading completes, opt in explicitly:

var records = Excel.Read<SalesRecord>(stream, leaveOpen: false).ToList();

🧭 Compile-Time Diagnostics

Id Severity Meaning
EXF001 Error A class marked [ExcelFlowSerializable] (or its enclosing type) is not partial
EXF002 Warning Property type is not natively supported; runtime falls back to Convert.ChangeType
EXF003 Error Generic classes are not supported

💉 Dependency Injection (ASP.NET Core)

ExcelFlow provides a built-in interface IExcelFlowService for easy Dependency Injection, making your code perfectly testable.

// Program.cs
builder.Services.AddExcelFlow();

Inject and use it in your controllers or services:

public class ReportController
{
    private readonly IExcelFlowService _excel;

    public ReportController(IExcelFlowService excel)
    {
        _excel = excel;
    }

    public async Task<IActionResult> Upload(IFormFile file)
    {
        var data = await _excel.Read<SalesRecord>(file.OpenReadStream()).ToListAsync();
        return Ok(data);
    }
}

🆕 What's new in 1.2.0

  • Packaging fix: the source generator now ships inside the NuGet package. If mappers were not generated for you on 1.1.x — this release fixes it.
  • Correct row numbers: ExcelParseError.RowNumber now reports the real 1-based sheet row, including when SkipRows() is used.
  • Robust headers: empty header cells no longer throw NullReferenceException.
  • Culture-stable dates: string dates parse with InvariantCulture on any server locale.
  • Dates look like dates: exported DateTime cells carry a date number format.
  • Stream ownership: your streams are no longer disposed by the library (opt back in with leaveOpen: false).
  • New types: Guid and enum properties supported natively; nullable numeric/date properties fixed in the writer.
  • Compile-time diagnostics: EXF001–EXF003.
  • Shared files: files opened by path use FileShare.Read — reading works even while the file is open in Excel.

📄 License

ExcelFlow is licensed under the MIT License. See the LICENSE file for more details.

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.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 152 7/15/2026
1.1.2 125 7/7/2026
1.1.1 120 7/7/2026
1.1.0 126 7/6/2026
1.0.3 133 6/2/2026
1.0.2 130 6/2/2026
1.0.1 130 6/2/2026
1.0.0 129 6/2/2026

v1.2.0
- FIX: the source generator is now packed into the NuGet package (analyzers/dotnet/cs); previously consumers of the package did not receive generated mappers.
- FIX: reading no longer throws NullReferenceException when a header cell is empty.
- FIX: ExcelParseError.RowNumber now reports the real 1-based sheet row when SkipRows() is used.
- FIX: DateTime values parsed from strings now use InvariantCulture (was CurrentCulture).
- FIX: written DateTime cells now carry a date number format and display as dates in Excel (was raw OADate numbers).
- FIX: nullable numeric/date properties (e.g. decimal?) no longer break the generated writer.
- CHANGE: Excel.Read(Stream) no longer disposes the caller's stream; pass leaveOpen: false to opt into the old behavior.
- CHANGE: OnError() can now be called multiple times; all handlers are invoked.
- NEW: Guid and enum properties are supported natively.
- NEW: compile-time diagnostics: EXF001 (class must be partial), EXF002 (unsupported property type), EXF003 (generic classes not supported).
- NEW: files opened by path are opened with FileShare.Read, so a file open in Excel can still be read.