Reading and Writing Excel Files in C# Without Office Interop

ยท unvell team
Reading and Writing Excel Files in C# Without Office Interop

If you have ever tried to generate an Excel report or import a spreadsheet from a .NET service, you have probably hit the same wall as everyone else: Microsoft.Office.Interop.Excel requires Office to be installed, wonโ€™t run on a server, and is unsupported in headless or container environments.

This article walks through the practical alternatives for working with .xlsx files in C# โ€” what each library is good at, where the rough edges are, and which one to pick for which job.


Why not Office Interop?

Microsoft.Office.Interop.Excel automates a real Excel process via COM. It works on a developer machine but is a bad fit for almost any production scenario:

  • Excel must be installed on the host
  • Microsoft explicitly does not support it in services or web apps (KB257757)
  • Each call crosses a COM boundary, so it is slow
  • Crashed Excel processes leak and accumulate over time

For server-side or cross-platform code, you want a library that reads and writes the OpenXML .xlsx format directly.


The contenders

LibraryLicenseWhat itโ€™s for
OpenXML SDKMITLow-level read/write of the raw OpenXML parts
ClosedXMLMITHigh-level wrapper over OpenXML SDK โ€” simple API
EPPlusPolyform Noncommercial / CommercialFull-featured xlsx, charts, pivots; paid for commercial use since v5
NPOIApache 2.0Java POI port โ€” handles both legacy .xls and .xlsx
ReoGridFree / CommercialRead/write and display/edit the same file in WinForms/WPF

The โ€œrightโ€ choice depends on whether you also need to show the spreadsheet to a user, not just parse it.


Reading a workbook

Suppose you have an invoices.xlsx and you want to print every row in the first sheet.

OpenXML SDK (low-level)

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

using var doc = SpreadsheetDocument.Open("invoices.xlsx", false);
var sheet = doc.WorkbookPart!.Workbook.Sheets!.Elements<Sheet>().First();
var part  = (WorksheetPart)doc.WorkbookPart.GetPartById(sheet.Id!);
var rows  = part.Worksheet.Descendants<Row>();

foreach (var row in rows)
{
    foreach (var cell in row.Elements<Cell>())
    {
        Console.Write(cell.CellValue?.Text + "\t");
    }
    Console.WriteLine();
}

This is the official Microsoft library and itโ€™s free, but you spend most of your time fighting the format: shared strings, styles indexed by position, inline strings, and so on. Use it when you need bit-level control.

ClosedXML (high-level wrapper)

using ClosedXML.Excel;

using var book = new XLWorkbook("invoices.xlsx");
var sheet = book.Worksheet(1);

foreach (var row in sheet.RowsUsed())
{
    foreach (var cell in row.CellsUsed())
    {
        Console.Write(cell.GetString() + "\t");
    }
    Console.WriteLine();
}

This is the experience most people want. The trade-off is that ClosedXML loads the whole workbook into memory and is slower than streaming readers for very large files.

ReoGrid (when you also display the data)

using unvell.ReoGrid;

var book = ReoGridControl.CreateMemoryWorkbook();
book.Load("invoices.xlsx");

var sheet = book.Worksheets[0];
for (int r = 0; r <= sheet.MaxContentRow; r++)
{
    for (int c = 0; c <= sheet.MaxContentCol; c++)
    {
        Console.Write(sheet[r, c] + "\t");
    }
    Console.WriteLine();
}

Same workbook object you bind to the WinForms/WPF grid control, so there is no separate โ€œview modelโ€ layer.


Writing a workbook

Now the inverse โ€” generate a small report with a header row, a few data rows, and an Excel formula.

ClosedXML

using var book = new XLWorkbook();
var sheet = book.Worksheets.Add("Report");

sheet.Cell("A1").Value = "Product";
sheet.Cell("B1").Value = "Units";
sheet.Cell("C1").Value = "Price";
sheet.Cell("D1").Value = "Total";
sheet.Range("A1:D1").Style.Font.Bold = true;

sheet.Cell("A2").Value = "Widget";
sheet.Cell("B2").Value = 12;
sheet.Cell("C2").Value = 9.99;
sheet.Cell("D2").FormulaA1 = "=B2*C2";

book.SaveAs("report.xlsx");

ReoGrid

var book = ReoGridControl.CreateMemoryWorkbook();
var sheet = book.CreateWorksheet("Report");
book.Worksheets.Add(sheet);

sheet["A1"] = new object[] { "Product", "Units", "Price", "Total" };
sheet.Ranges["A1:D1"].Style.Bold = true;

sheet["A2"] = new object[] { "Widget", 12, 9.99 };
sheet["D2"] = "=B2*C2";

book.Save("report.xlsx");

Both libraries handle column widths, fills, borders, number formats, and merged cells the way you would expect. The biggest difference is that ReoGrid evaluates the formula at write time using its built-in formula engine, so D2 already contains 119.88 when the file lands on disk. With ClosedXML you need book.RecalculateAllFormulas() first if you depend on cached values.


What about really large files?

For files where you cannot afford to load everything into memory:

  • OpenXML SDK has the OpenXmlReader for streaming โ€” verbose but cheap.
  • EPPlus has a streaming write API that flushes rows as you append them.
  • ReoGrid 4.4 added bulk loading via SetRangeData that is around 3ร— faster than per-cell assignment for 200,000-cell loads โ€” see the v4.4 release notes for measured numbers.

Rule of thumb: under 50,000 rows the friendly APIs are fine; above that, stream.


Picking a library

A short decision guide:

  • Just need to parse a known-format xlsx? โ†’ ClosedXML or NPOI. Both are MIT/Apache and easy.
  • Need full control over OpenXML internals? โ†’ OpenXML SDK.
  • Need to also display and edit the file in a desktop app? โ†’ ReoGrid (or any spreadsheet UI control).
  • Need charts, pivots, conditional formatting, all on the server? โ†’ EPPlus (commercial license required) or ReoGrid.
  • Mixing .xls and .xlsx? โ†’ NPOI is the only one with first-class legacy .xls support.

There isnโ€™t one library that wins on every axis. For headless reporting in a service, ClosedXML is hard to beat. For a desktop application that loads, edits, and saves the same workbook, having one library that does both โ€” read/write and UI โ€” is much less code than gluing two libraries together.


Further reading

Related articles

Try ReoGrid in your own project

The Excel-compatible spreadsheet component for .NET WinForms and WPF. 30-day free trial โ€” no credit card required.