Skip to content

TriasDev.Tabular

Fast, low-memory reading and writing of csv, Excel (.xlsx) and OpenDocument (.ods) files for .NET — with no dependencies.

The library has two halves:

  • Import — reads xlsx, ods and csv, alone or inside zip, tar and gzip archives; profiles every column over every row (types, emptiness, uniqueness, ranges, the rows that do not fit); imports typed rows through a column mapping a person confirmed, with errors that name their row, column and code.
  • Export — writes csv, xlsx, ods and a zip of csv sheets, streaming and asynchronously into any stream — a file, a blob, an ASP.NET Core response — with typed columns, cell styles, frozen headers and auto-filters. What it writes reads back through its own import; a value a format cannot hold exactly is refused, never rounded.

Multi-million-row files are read in seconds, memory stays flat in both directions as files grow, and everything is built on the base class library alone: no third-party packages.

dotnet add package TriasDev.Tabular

Reading — declare the fields once, profile the file, build a mapping plan from its headers; then import typed rows or located errors through it:

// The fields, declared once: they build the schema and read the values.
TextImportField name = ImportField.Text("name").Require().MaxLength(100);
TextImportField country = ImportField.Text("country").ExactLength(2);
DateImportField signedOn = ImportField.Date("signed_on");
DecimalImportField amount = ImportField.Decimal("amount").Require();
ImportSchema schema = new() { Fields = [name, country, signedOn, amount] };
// 1. Profile the file, and build the plan from its headers.
FileProfile profile;
using (FileStream file = File.OpenRead(path))
using (ITabularCursor cursor = TabularFile.Open(file, Path.GetFileName(path)))
{
    profile = TabularAnalyzer.Analyze(cursor);
}

MappingPlan plan = MappingPlan.ByHeader(profile.Sheets[0], schema, culture: "de-DE");

Writing — declare the columns once, with a style rule and a frozen, filtered header, and stream chunks of objects into a workbook (exporting):

private static readonly CellStyle Late = new() { Fill = CellColor.Parse("#FFC7CE") };

private static readonly CellStyle Header = new() { Fill = CellColor.Parse("#1F4E78"), Font = new CellFont { Bold = true, Color = CellColor.Parse("#FFFFFF") } };

// Declared once, kept in a static field, shared by any number of concurrent writes.
public static readonly TabularExport<Order> Export = TabularExport.For<Order>()
    .Column("Id", o => o.Id, width: 8)
    .Column("Customer", o => o.Customer, width: 28)
    .Column("Placed", o => o.Placed)
    .Column("Amount", o => o.Amount, width: 12)
    .Column("Status", o => o.Status, width: 10, style: status => status == "late" ? Late : null)
    .Column("Paid", o => o.Paid)
    .Sheet(new SheetOptions { HeaderStyle = Header, FreezeRows = 1, AutoFilter = true })
    .Build();
public static async Task<long> WriteChunksAsync(Stream stream, IAsyncEnumerable<IReadOnlyList<Order>> chunks, CancellationToken cancellationToken) =>
    await Export.WriteAsync(stream, TabularFormat.Xlsx, "Orders", chunks, cancellationToken: cancellationToken);

Choose your path

I am adding an upload to my application

A user uploads a spreadsheet; you profile it, let them map its columns to your fields, check the mapping, and import typed rows or precise errors.

  • Getting started — install, profile, map, check, import; write a file
  • How it works — two independent reads of the file, facts against suggestions
  • Importing — the run, batches, rules, translated fields, field alternatives, the precheck and the review
  • Error codes — every code a row error or an exception carries

I just need to read big files fast

A forward-only cursor over rows of typed cells, for xlsx, ods, csv, zip and tar archives of them, and gzip-compressed files.

  • Formats — what each kind of file reads as, and how malformed csv is repaired
  • Performance — the library's own numbers
  • Benchmarks — against Sylvan, Sep, CsvHelper, ExcelDataReader, MiniExcel, ClosedXML, NPOI and EPPlus

I need to hand files back

Csv, xlsx, ods or a zip of csv sheets, written row by row, from objects declared once, or by column — styled, streamed into a response or a blob, in flat memory.

  • Exporting — the writer, declared exports, data by column, styles, sheet layout, where the file goes, failures
  • Formats — what each format holds when written
  • Benchmarks — against CsvHelper, Sep, Sylvan, LargeXlsx, SpreadCheetah and MiniExcel

I need to know what it will refuse

For AI agents

The documentation is also published for language models, following llms.txt: llms.txt is a short map of the library, and llms-full.txt is every page in one Markdown file. Each page is also available as Markdown next to its HTML.

Source

github.com/TriasDev/tabular · MIT licence · NuGet