DEV Community

IronSoftware
IronSoftware

Posted on

C# Excel Generation: The Real Cost of Using the Free Open XML SDK

Every team that generates spreadsheets in .NET eventually runs the same evaluation. There is a free, Microsoft-backed option sitting right there, and there is a commercial one that costs money per deployment. On a spreadsheet of first-order costs, the free option wins before the meeting even starts. The problem is that the interesting costs are second-order, and they do not show up until you are three sprints into building a reporting service. This is a build-versus-buy story, and the fair version of it starts by admitting that Open XML SDK is genuinely excellent at the thing it was designed to do.

At Iron Software, we develop IronXL. We will point out cases where the Open XML SDK is more suitable, as it performs best for specific tasks.

What Open XML SDK Actually Is

DocumentFormat.OpenXml is a low-level schema API. It is shipped by Microsoft under the .NET Foundation, it is MIT-licensed, and the current release is 3.5.1 from March 2026 with more than 1,300 commits behind it. It tracks the OOXML schema that Office itself uses, which means the objects you work with are strongly typed and generated directly from the ECMA-376 and ISO/IEC 29500 standards. When the standard changes, the object model follows.

That design has real, concrete advantages, and we want to name them before we get anywhere near the gaps.

  • ✅ The strongly typed object model means the compiler catches malformed structure. You are manipulating the same schema Word and Excel serialize, not a friendly approximation of it.
  • ✅ There is a built-in OpenXmlValidator that checks byte-level structural fidelity, which is the kind of guarantee you want when a downstream system is strict about the format.
  • ✅ The MIT license carries zero revenue, seat, or SaaS restrictions. You can ship it in a product sold to a million customers and owe nothing and ask no one.
  • ✅ The Open Packaging Conventions give you random access to individual document parts. You can strip the comments out of a docx or move a single slide inside a deck without parsing the rest of the file.

If your work is document surgery, schema validation, or high-volume structural transformation where licensing risk at scale is the thing keeping you up at night, this is the correct tool and nothing we sell changes that. The README is honest about the contract. It expects you to bring detailed knowledge of the relevant formats and standards. It hands you the schema and, deliberately, nothing more.

The architecture question is what "nothing more" costs to build.

The First Gap Is the Expensive One. There Is No Calculation Engine.

This is the gap that surprises people, so it is worth being precise about the mechanics. In OOXML, a cell that contains a formula stores two separate things. CellFormula holds the formula text, something like SUM(B1:B10). CellValue holds a cached result, and that cached number was computed by Excel, not by the SDK. Open XML SDK reads and writes both fields faithfully. What it never does is calculate.

Play that forward. You build a workbook in a headless service, you write the formula text into every total cell, and you save the file. Because your service never computed a result, there is no cached value to write, so those cells are blank or stale on disk. They stay blank until a human opens the file in Excel and Excel recalculates on load. On a server that generates an invoice and emails it straight to a customer, no human ever opens it first. The customer receives a document where the total is empty.

The workaround people reach for is to compute every value in their own C# and write it into CellValue themselves, keeping the formula text alongside only for show. That works, but read what it actually is. You have written a spreadsheet calculation engine. You own SUM and AVERAGE and the operator precedence and the cell-reference resolution and the edge cases around blanks and text-in-numeric-cells, forever, in your application code. That is a large surface to build and a larger one to keep correct.

This is not an oversight that a patch is about to close. The request for a calculation feature has been open since 2018. A formal API proposal was still open in late 2025. This is a matter of roadmap scope rather than neglect, and the safe reading is that you should not architect around it landing. If your workload needs computed results, plan to supply that layer yourself or pick a library that already has one. For teams that live in formulas, our own guide to copying and working with formulas shows the behavior we mean when we say the result should exist the moment the file is written.

The Second Gap Is Legacy Formats. It Is OOXML or Nothing.

Open XML SDK reads and writes the modern XML-based formats and only those. There is no support for the old binary formats, no .xls, no .doc, no .ppt, in either direction. If your pipeline receives files from the outside world, this matters more than it looks.

Consider the common case. A partner or an internal legacy system uploads an .xls file, the binary format that was standard for years and still turns up constantly in finance, logistics, and government data. Hand that file to Open XML SDK and it cannot open it, because it is not OOXML. Your pipeline now needs a separate conversion step in front of the SDK, some other component that turns .xls into .xlsx before the SDK is allowed to see it. That is another dependency, another failure point, and another thing to test against the messy real-world files that never quite match the spec.

None of this is a bug. It is the scope Microsoft chose, and it is a reasonable scope for a tool named after the Open XML standard. It just means the format-conversion layer is, once again, yours to build or source. We wrote up the .xls to .xlsx conversion problem precisely because so many teams hit it the week after they commit to an OOXML-only stack.

The Third Gap Is Memory. The Default API Buffers Everything.

The friendly, discoverable way to use Open XML SDK is the DOM API, the one in every tutorial where you build up Row and Cell objects and append them to SheetData. It is readable and it is the approach the sample below uses. It also loads the entire document part into memory as an object tree.

On a normal report this is fine. On a workbook with hundreds of thousands of rows it is an OutOfMemoryException, and Microsoft's own position is that this is expected behavior for a DOM. A DOM materializes the whole tree by definition, so a big enough tree exhausts the heap. There is nothing broken here, only a boundary you have to know about before you cross it in production.

The supported fix is to drop from the DOM down to the SAX-style OpenXmlReader and OpenXmlWriter. These stream the document element by element and keep memory flat, and they work correctly. The cost is that the streaming API is materially more verbose and lower level than the DOM. Your clean object-building code becomes a state machine of read-element and write-element calls, and the thing you were trying to express gets buried under the mechanics of the stream. You end up maintaining two mental models, the pretty one for small files and the painful one for large files, and choosing between them per feature. When we handle large exports from a DataTable we want one path that does not fall over at scale, not a fork in the code.

What This Looks Like in Code

Here is the SDK writing a single labeled cell into a new workbook. It is verbose by design, and we have kept it that way rather than trim it, because the verbosity is the point. Every part of the package is something you name and wire up by hand.

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

// Create the package and the workbook part
using var document = SpreadsheetDocument.Create("report.xlsx", SpreadsheetDocumentType.Workbook);
var workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();

// Add a worksheet part and its sheet data container
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);

// Register the sheet in the workbook's sheet list
var sheets = workbookPart.Workbook.AppendChild(new Sheets());
sheets.Append(new Sheet { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Report" });

// Build one row with one string cell
var row = new Row { RowIndex = 1 };
row.Append(new Cell { CellReference = "A1", DataType = CellValues.String, CellValue = new CellValue("Total Due") });
sheetData.Append(row);

workbookPart.Workbook.Save();
Enter fullscreen mode Exit fullscreen mode

Now the same neighborhood of work in IronXL, including a formula that is actually computed and read back as a number before the file is ever saved.

using IronXL;

// Load an existing workbook, including legacy .xls if that is what arrives
WorkBook workBook = WorkBook.Load("invoice.xlsx");
WorkSheet workSheet = workBook.DefaultWorkSheet;

// Write a label, a value, and a formula
workSheet["A1"].Value = "Total Due";
workSheet["B1"].Value = 250.00m;
workSheet["B2"].Formula = "=SUM(B1:B1)";

// Calculate every formula in the book without Excel installed
workBook.EvaluateAll();
decimal total = workSheet["B2"].DecimalValue;

workBook.SaveAs("invoice-updated.xlsx");
Enter fullscreen mode Exit fullscreen mode

The difference that matters is the one line EvaluateAll(). IronXL computes formulas itself with no Excel on the machine, so total is a real number the instant that call returns. WorkBook.Load opens .xls in the same breath as .xlsx, so the conversion step disappears. And the large-file memory management happens inside the library rather than in a streaming state machine you maintain. The reading walkthrough shows the same shape across more of the surface, and the full API documentation covers the rest.

The Comparison, Both Sides

Concern Open XML SDK IronXL
License MIT, no seat or revenue limits at any scale Commercial, tiered by deployment, no free tier
Formula calculation None. You compute values and cache them yourself Built in via EvaluateAll(), no Excel needed
Legacy .xls read and write Not supported, OOXML only Loads .xls alongside .xlsx
Large files DOM buffers in memory, drop to SAX to stream Managed internally on the default path
Schema and structural fidelity Strongly typed, built-in validator Higher-level model over the same formats
API level Low level, expects format knowledge Task-level, fewer moving parts
Backing Microsoft, .NET Foundation, active Iron Software, commercial support

A quick word on things that get miscounted against the SDK. It has no CVEs of its own. The CVE-2023-21538 that turns up in an issue thread is a denial-of-service in the .NET runtime's System.Private.DataContractSerialization, not in the SDK's code. And the discontinued "Microsoft Open XML File Format Converter", an old Mac Office tool, is a naming coincidence with no relationship to this library. The maintenance story is genuinely healthy and corporate-backed. The formula gap staying open since 2018 is a scope decision, not a sign of an abandoned project.

So What Should You Actually Pick

Reframe it as a total-cost-of-ownership question and the answer stops being universal. If you need schema-level control, structural validation, part-by-part surgery on documents, or zero licensing exposure across an unbounded number of installs, Open XML SDK is the stronger choice and the price is unbeatable. You are trading engineering time for license fees, and for that class of work the trade is sound.

The trade inverts when your service needs computed totals, ingests legacy binaries, or streams very large workbooks under memory pressure. There you are not choosing between free and paid. You are choosing between building and maintaining a calculation engine, a format-conversion layer, and a memory strategy, versus buying those three layers already built and tested. IronXL is commercial and there is no free tier, so it is a real budget line, and you can read exactly how the tiers work on the licensing page. The comparison worth making is that cost against the salaried months your own team would spend rebuilding the layers the SDK deliberately leaves out.

The mistake we watch teams make is pricing only the license and never the layer. When you have shipped one calculation engine of your own, which of the three gaps would cost your team the most to close, and did that surprise you when you first hit it in production? We would genuinely like to read your war stories in the comments.

If you are weighing this decision right now, try the two approaches on your own worst file, the ugliest legacy .xls with the deepest formulas you have, and tell us in the comments how each one held up.

Open XML SDK is maintained by Microsoft under the .NET Foundation, and the names referenced are the property of their owners. We are not affiliated with Microsoft, and the details above draw on the SDK's public documentation, repository, and issue tracker as they stood at the time of writing. If a version or a date has moved since, correct us in the comments.

Top comments (0)