.NET のサービスから Excel レポートを生成しようとした人、もしくはスプレッドシートを取り込もうとした人なら、おそらく同じ壁に突き当たったはずです。Microsoft.Office.Interop.Excel を使うには Office のインストールが必要で、サーバーでは動かず、ヘッドレス環境やコンテナ環境ではサポートされません。
本記事では、C# で .xlsx を扱うための実用的な選択肢を整理します。それぞれのライブラリが何に向いていて、どこに落とし穴があるのか、そして「どのケースにどれを選ぶべきか」を解説します。
なぜ Office Interop ではダメなのか
Microsoft.Office.Interop.Excel は COM を介して実物の Excel プロセスを自動操作します。開発者のマシンでは動くものの、本番環境ではほぼすべてのシナリオで不適切です。
- ホストに Excel がインストールされている必要がある
- Microsoft 自身がサービスや Web アプリでの利用をサポートしていない(KB257757)
- 呼び出しのたびに COM 境界を越えるため遅い
- クラッシュした Excel プロセスが残り、徐々に蓄積していく
サーバーサイドやクロスプラットフォームのコードでは、OpenXML の .xlsx 形式を 直接 読み書きするライブラリを選ぶのが基本方針になります。
候補一覧
| ライブラリ | ライセンス | 用途 |
|---|---|---|
| OpenXML SDK | MIT | OpenXML パーツの低レベル読み書き |
| ClosedXML | MIT | OpenXML SDK をラップした高レベル API |
| EPPlus | Polyform Noncommercial / 商用 | 高機能な xlsx・グラフ・ピボット。v5 以降は 商用利用は有償 |
| NPOI | Apache 2.0 | Java POI の移植版。レガシーな .xls と .xlsx の両方を扱える |
| ReoGrid | フリー / 商用 | WinForms / WPF で 同じファイルを読み書きしつつ表示・編集 できる |
正解は、「単に解析するだけ」なのか「ユーザーに表示する必要もある」のかで決まります。
ワークブックを読み込む
invoices.xlsx の最初のシートのすべての行を出力したい、というケースを想定します。
OpenXML SDK(低レベル)
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();
}
Microsoft 公式の無償ライブラリですが、ほとんどの時間を「フォーマットとの戦い」に費やすことになります。共有文字列、インデックス指定のスタイル、インライン文字列など。ビット単位の制御が必要なとき に向いています。
ClosedXML(高レベルラッパ)
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();
}
これが多くの人にとって「欲しい体験」のはずです。トレードオフは、ClosedXML がワークブック全体をメモリに読み込むため、非常に大きいファイル ではストリーミング系のリーダより遅くなる点です。
ReoGrid(表示も伴うとき)
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();
}
WinForms / WPF のグリッドコントロールにバインドするのと 同じワークブックオブジェクト なので、別途「ビューモデル層」を持たなくて済みます。
ワークブックを書き出す
逆に、ヘッダー行・数行のデータ・Excel 数式を含む小さなレポートを生成してみます。
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");
どちらも、列幅・塗りつぶし・罫線・数値書式・結合セルなどは想定どおりに扱えます。最大の違いは、ReoGrid は内蔵の 数式エンジン で書き出し時に数式を評価するため、ファイルがディスクに着地した時点で D2 には既に 119.88 が入っている、という点です。ClosedXML でキャッシュ値が必要な場合は、事前に book.RecalculateAllFormulas() を呼ぶ必要があります。
非常に大きなファイルへの対応
メモリにすべてを乗せられないケースでは、次のような手段があります。
- OpenXML SDK にはストリーミング向けの
OpenXmlReaderがあります。記述量は多いですが省メモリです。 - EPPlus は、行を追記するごとにフラッシュするストリーミング書き込み API を備えています。
- ReoGrid 4.4 では、20 万セル規模の一括ロードがセル単位の代入比で約 3 倍速い
SetRangeDataが追加されています。実測値は v4.4 リリースノート を参照してください。
経験則: 5 万行以下 ならフレンドリーな API で十分。それ以上はストリーミングを検討してください。
ライブラリの選び方
簡単な意思決定ガイド:
- 既知フォーマットの xlsx をパースしたいだけ? → ClosedXML か NPOI。MIT / Apache で扱いやすい。
- OpenXML 内部を完全に制御したい? → OpenXML SDK。
- デスクトップアプリで表示・編集も行いたい? → ReoGrid(または他のスプレッドシート UI コントロール)。
- サーバーでグラフ・ピボット・条件付き書式まで使いたい? → EPPlus(商用ライセンス必要)または ReoGrid。
.xlsと.xlsxを併用? → レガシー.xlsを一級市民として扱えるのは NPOI のみ。
すべての軸で勝つ単一のライブラリはありません。サービス内でのヘッドレスなレポート生成なら ClosedXML がほぼ最適です。デスクトップアプリで「同じワークブックを読み込み・編集・保存する」なら、読み書きと UI を 1 つのライブラリで こなせる選択肢のほうが、2 つを糊で繋ぐよりずっと少ないコードで済みます。
