How to Read Excel Files in C# Using IronXL
Reading Excel files in C# applications is a recurring requirement for business software, data processing pipelines, and reporting systems. Traditional approaches using Microsoft Office Interop require Excel installed on every server or workstation, creating brittle dependencies that complicate deployment and licensing. IronXL removes that dependency entirely -- your application reads XLSX, XLS, CSV, and other spreadsheet formats without Office installed anywhere in the environment.
This tutorial walks you through every technique you need: installing the library, loading workbooks, extracting typed cell values, iterating rows and columns, running aggregate calculations, and building a complete employee-data reader. All examples use C# with top-level statements targeting .NET 10.
How Do You Install IronXL for Excel Processing?
Open the NuGet Package Manager Console in Visual Studio and run the following command to add IronXL to your project. Alternatively, if you prefer working from a terminal, use the .NET CLI command shown on the second line:
Install-Package IronXL.Excel
dotnet add package IronXL.Excel
Install-Package IronXL.Excel
dotnet add package IronXL.Excel
The package installs in seconds and adds a single managed assembly reference to your project. No COM registration, no Office Primary Interop Assemblies, and no version-specific Excel dependencies to manage.
After installation, add the using directive at the top of each file that works with spreadsheet data:
using IronXL;
using IronXL;
Imports IronXL
That single namespace gives you access to WorkBook, WorkSheet, cell ranges, typed value properties, aggregate functions, and every other IronXL type. The NuGet Gallery page for IronXL.Excel lists every available release and its full dependency tree.
What Excel File Formats Does IronXL Support?
Understanding the formats your application must handle shapes both the API calls you make and the storage decisions your project adopts.
XLSX -- the default format since Excel 2007 -- is a ZIP archive of XML files. It supports more than one million rows, rich formatting, pivot tables, and named ranges. Most modern data pipelines produce XLSX output, so this is the format you encounter most often.
XLS is the legacy binary format used by Excel 2003 and earlier. Some enterprise systems still export XLS, so reliable support for this format matters when integrating with older infrastructure. IronXL reads XLS files without any special configuration on your part.
XLSM extends XLSX with macro support. IronXL reads the spreadsheet data from XLSM files even if it does not execute the embedded VBA code, which is the correct behavior for server-side data extraction.
CSV and TSV are plain-text tabular formats widely used for data interchange between systems. WorkBook.LoadCSV handles comma-separated files, and IronXL returns the same WorkBook object your code already knows how to navigate, keeping your data-reading logic uniform regardless of the source format.
IronXL detects the file format automatically from the file extension when you call WorkBook.Load. You do not need to specify the format explicitly in most cases, which simplifies code that must handle inputs arriving from multiple sources.
| Format | Extension | Load Method | Notes |
|---|---|---|---|
| Open XML Workbook | .xlsx | WorkBook.Load | Default modern format |
| Legacy Binary | .xls | WorkBook.Load | Excel 2003 and earlier |
| Macro-enabled | .xlsm | WorkBook.Load | Data read; macros not executed |
| Comma-separated | .csv | WorkBook.LoadCSV | Optimized CSV parser |
| Tab-separated | .tsv | WorkBook.Load | Tab delimiter variant |
How Do You Load and Read an Excel Workbook?
The WorkBook.Load method is the entry point for all file-based operations. Pass the file path and IronXL returns a fully populated workbook object you can immediately query:
using IronXL;
// Load the workbook; format is detected automatically
WorkBook workbook = WorkBook.Load("Employees.xlsx");
// Access the first worksheet
WorkSheet sheet = workbook.DefaultWorkSheet;
// Read a single cell by A1 address
string companyName = sheet["A1"].StringValue;
Console.WriteLine($"Company: {companyName}");
// Access sheets by name or by zero-based index
WorkSheet byName = workbook.GetWorkSheet("Sales");
WorkSheet byIndex = workbook.WorkSheets[1];
// Load a password-protected file
WorkBook secured = WorkBook.Load("Confidential.xlsx", "p@ssw0rd");
using IronXL;
// Load the workbook; format is detected automatically
WorkBook workbook = WorkBook.Load("Employees.xlsx");
// Access the first worksheet
WorkSheet sheet = workbook.DefaultWorkSheet;
// Read a single cell by A1 address
string companyName = sheet["A1"].StringValue;
Console.WriteLine($"Company: {companyName}");
// Access sheets by name or by zero-based index
WorkSheet byName = workbook.GetWorkSheet("Sales");
WorkSheet byIndex = workbook.WorkSheets[1];
// Load a password-protected file
WorkBook secured = WorkBook.Load("Confidential.xlsx", "p@ssw0rd");
Imports IronXL
' Load the workbook; format is detected automatically
Dim workbook As WorkBook = WorkBook.Load("Employees.xlsx")
' Access the first worksheet
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
' Read a single cell by A1 address
Dim companyName As String = sheet("A1").StringValue
Console.WriteLine($"Company: {companyName}")
' Access sheets by name or by zero-based index
Dim byName As WorkSheet = workbook.GetWorkSheet("Sales")
Dim byIndex As WorkSheet = workbook.WorkSheets(1)
' Load a password-protected file
Dim secured As WorkBook = WorkBook.Load("Confidential.xlsx", "p@ssw0rd")
The DefaultWorkSheet property returns the first sheet, covering the majority of single-sheet files. When your file contains multiple sheets, GetWorkSheet retrieves by name and WorkSheets[n] retrieves by zero-based index. The WorkBook API reference documents every overload and property on the workbook object.

Input

Output

How Do You Read Typed Cell Values from Excel?
Every cell in IronXL exposes typed properties that return values in the exact data type you need -- no manual parsing or casting required. The typed properties handle coercion from the underlying cell representation automatically:
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Extract typed values from row 2
int employeeId = sheet["A2"].IntValue;
string name = sheet["B2"].StringValue;
string department = sheet["C2"].StringValue;
decimal salary = sheet["D2"].DecimalValue;
DateTime hireDate = sheet["E2"].DateTimeValue;
Console.WriteLine($"ID: {employeeId}");
Console.WriteLine($"Name: {name}");
Console.WriteLine($"Department: {department}");
Console.WriteLine($"Salary: {salary:C}");
Console.WriteLine($"Hired: {hireDate:d}");
// Use the Text property for the formatted display value
string formattedSalary = sheet["D2"].Text; // "$75,000.00"
string formattedDate = sheet["E2"].Text; // "28/02/2026"
// Guard against empty cells before reading
if (!sheet["B3"].IsEmpty)
{
string value = sheet["B3"].StringValue;
}
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Extract typed values from row 2
int employeeId = sheet["A2"].IntValue;
string name = sheet["B2"].StringValue;
string department = sheet["C2"].StringValue;
decimal salary = sheet["D2"].DecimalValue;
DateTime hireDate = sheet["E2"].DateTimeValue;
Console.WriteLine($"ID: {employeeId}");
Console.WriteLine($"Name: {name}");
Console.WriteLine($"Department: {department}");
Console.WriteLine($"Salary: {salary:C}");
Console.WriteLine($"Hired: {hireDate:d}");
// Use the Text property for the formatted display value
string formattedSalary = sheet["D2"].Text; // "$75,000.00"
string formattedDate = sheet["E2"].Text; // "28/02/2026"
// Guard against empty cells before reading
if (!sheet["B3"].IsEmpty)
{
string value = sheet["B3"].StringValue;
}
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("Employees.xlsx")
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
' Extract typed values from row 2
Dim employeeId As Integer = sheet("A2").IntValue
Dim name As String = sheet("B2").StringValue
Dim department As String = sheet("C2").StringValue
Dim salary As Decimal = sheet("D2").DecimalValue
Dim hireDate As DateTime = sheet("E2").DateTimeValue
Console.WriteLine($"ID: {employeeId}")
Console.WriteLine($"Name: {name}")
Console.WriteLine($"Department: {department}")
Console.WriteLine($"Salary: {salary:C}")
Console.WriteLine($"Hired: {hireDate:d}")
' Use the Text property for the formatted display value
Dim formattedSalary As String = sheet("D2").Text ' "$75,000.00"
Dim formattedDate As String = sheet("E2").Text ' "28/02/2026"
' Guard against empty cells before reading
If Not sheet("B3").IsEmpty Then
Dim value As String = sheet("B3").StringValue
End If
The Text property returns the display value exactly as formatted in Excel -- useful when you need the formatted number string rather than the raw numeric. For cells that contain Excel formulas, IronXL evaluates the expression and returns the computed result through the same typed properties. Additional value properties -- BoolValue, DoubleValue, FloatValue -- appear in the cell value reference.
How Do You Iterate Through Excel Rows and Columns?
Processing every record in a dataset requires iterating across rows and columns. IronXL's range syntax maps directly onto Excel's A1 notation, so selecting a block of cells is familiar to anyone who has written spreadsheet formulas:
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Iterate a single column range
Console.WriteLine("Employee List:");
foreach (var cell in sheet["B2:B100"])
{
if (!string.IsNullOrEmpty(cell.Text))
Console.WriteLine($" - {cell.Text}");
}
// Iterate all rows and all columns using the Rows property
foreach (var row in sheet.Rows)
{
foreach (var cell in row)
Console.Write(cell.Text.PadRight(15));
Console.WriteLine();
}
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Iterate a single column range
Console.WriteLine("Employee List:");
foreach (var cell in sheet["B2:B100"])
{
if (!string.IsNullOrEmpty(cell.Text))
Console.WriteLine($" - {cell.Text}");
}
// Iterate all rows and all columns using the Rows property
foreach (var row in sheet.Rows)
{
foreach (var cell in row)
Console.Write(cell.Text.PadRight(15));
Console.WriteLine();
}
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("Employees.xlsx")
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
' Iterate a single column range
Console.WriteLine("Employee List:")
For Each cell In sheet("B2:B100")
If Not String.IsNullOrEmpty(cell.Text) Then
Console.WriteLine($" - {cell.Text}")
End If
Next
' Iterate all rows and all columns using the Rows property
For Each row In sheet.Rows
For Each cell In row
Console.Write(cell.Text.PadRight(15))
Next
Console.WriteLine()
Next
The range sheet["B2:B100"] returns an enumerable collection of cell objects. Checking string.IsNullOrEmpty skips blank rows gracefully. The Rows property -- documented in the worksheet data range guide -- walks the sheet one row at a time and exposes each row's cells without requiring you to know the column count in advance.
Output

How Do You Run Aggregate Calculations on Excel Data?
IronXL includes built-in aggregate functions that compute results directly on cell ranges -- no manual accumulation loops required:
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Built-in aggregate methods work on any named range
decimal total = sheet["D2:D200"].Sum();
decimal highest = sheet["D2:D200"].Max();
decimal lowest = sheet["D2:D200"].Min();
decimal average = sheet["D2:D200"].Avg();
Console.WriteLine($"Total Payroll: {total:C}");
Console.WriteLine($"Highest Salary: {highest:C}");
Console.WriteLine($"Lowest Salary: {lowest:C}");
Console.WriteLine($"Average Salary: {average:C}");
// Combine with LINQ for filtered aggregation
decimal itTotal = sheet["D2:D200"]
.Where(c => sheet[$"C{c.RowIndex}"].StringValue == "IT")
.Sum(c => c.DecimalValue);
Console.WriteLine($"IT Department Total: {itTotal:C}");
using IronXL;
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
// Built-in aggregate methods work on any named range
decimal total = sheet["D2:D200"].Sum();
decimal highest = sheet["D2:D200"].Max();
decimal lowest = sheet["D2:D200"].Min();
decimal average = sheet["D2:D200"].Avg();
Console.WriteLine($"Total Payroll: {total:C}");
Console.WriteLine($"Highest Salary: {highest:C}");
Console.WriteLine($"Lowest Salary: {lowest:C}");
Console.WriteLine($"Average Salary: {average:C}");
// Combine with LINQ for filtered aggregation
decimal itTotal = sheet["D2:D200"]
.Where(c => sheet[$"C{c.RowIndex}"].StringValue == "IT")
.Sum(c => c.DecimalValue);
Console.WriteLine($"IT Department Total: {itTotal:C}");
Imports IronXL
Dim workbook As WorkBook = WorkBook.Load("Employees.xlsx")
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
' Built-in aggregate methods work on any named range
Dim total As Decimal = sheet("D2:D200").Sum()
Dim highest As Decimal = sheet("D2:D200").Max()
Dim lowest As Decimal = sheet("D2:D200").Min()
Dim average As Decimal = sheet("D2:D200").Avg()
Console.WriteLine($"Total Payroll: {total:C}")
Console.WriteLine($"Highest Salary: {highest:C}")
Console.WriteLine($"Lowest Salary: {lowest:C}")
Console.WriteLine($"Average Salary: {average:C}")
' Combine with LINQ for filtered aggregation
Dim itTotal As Decimal = sheet("D2:D200") _
.Where(Function(c) sheet($"C{c.RowIndex}").StringValue = "IT") _
.Sum(Function(c) c.DecimalValue)
Console.WriteLine($"IT Department Total: {itTotal:C}")
Sum, Min, Max, and Avg automatically skip empty cells and handle numeric conversion behind the scenes. Chaining LINQ queries onto the range gives you filtered aggregates -- department subtotals, date-range sums, and conditional counts -- all without leaving the IronXL API.
How Do You Build a Complete Employee Data Reader?
The following example assembles everything covered so far into a production-ready console application that loads an employee spreadsheet, formats each record, accumulates salary totals, handles write-back, and catches errors gracefully:
using IronXL;
try
{
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
Console.WriteLine("=== Employee Data Report ===\n");
int rowNumber = 2;
decimal totalSalary = 0m;
int employeeCount = 0;
while (!string.IsNullOrEmpty(sheet[$"A{rowNumber}"].StringValue))
{
int id = sheet[$"A{rowNumber}"].IntValue;
string name = sheet[$"B{rowNumber}"].StringValue;
string department = sheet[$"C{rowNumber}"].StringValue;
decimal salary = sheet[$"D{rowNumber}"].DecimalValue;
Console.WriteLine($"{id,4} {name,-20} {department,-15} {salary,10:C}");
totalSalary += salary;
employeeCount += 1;
rowNumber += 1;
}
Console.WriteLine($"\nTotal Employees: {employeeCount}");
Console.WriteLine($"Total Payroll: {totalSalary:C}");
// Write a new record back to the sheet and save
sheet["A10"].Value = 1010;
sheet["B10"].Value = "Jane Doe";
sheet["C10"].Value = "Finance";
sheet["D10"].Value = 75000;
workbook.SaveAs("Employees-Updated.xlsx");
}
catch (Exception ex)
{
Console.WriteLine($"Error reading Excel file: {ex.Message}");
}
using IronXL;
try
{
WorkBook workbook = WorkBook.Load("Employees.xlsx");
WorkSheet sheet = workbook.DefaultWorkSheet;
Console.WriteLine("=== Employee Data Report ===\n");
int rowNumber = 2;
decimal totalSalary = 0m;
int employeeCount = 0;
while (!string.IsNullOrEmpty(sheet[$"A{rowNumber}"].StringValue))
{
int id = sheet[$"A{rowNumber}"].IntValue;
string name = sheet[$"B{rowNumber}"].StringValue;
string department = sheet[$"C{rowNumber}"].StringValue;
decimal salary = sheet[$"D{rowNumber}"].DecimalValue;
Console.WriteLine($"{id,4} {name,-20} {department,-15} {salary,10:C}");
totalSalary += salary;
employeeCount += 1;
rowNumber += 1;
}
Console.WriteLine($"\nTotal Employees: {employeeCount}");
Console.WriteLine($"Total Payroll: {totalSalary:C}");
// Write a new record back to the sheet and save
sheet["A10"].Value = 1010;
sheet["B10"].Value = "Jane Doe";
sheet["C10"].Value = "Finance";
sheet["D10"].Value = 75000;
workbook.SaveAs("Employees-Updated.xlsx");
}
catch (Exception ex)
{
Console.WriteLine($"Error reading Excel file: {ex.Message}");
}
Imports IronXL
Try
Dim workbook As WorkBook = WorkBook.Load("Employees.xlsx")
Dim sheet As WorkSheet = workbook.DefaultWorkSheet
Console.WriteLine("=== Employee Data Report ===" & vbCrLf)
Dim rowNumber As Integer = 2
Dim totalSalary As Decimal = 0D
Dim employeeCount As Integer = 0
While Not String.IsNullOrEmpty(sheet($"A{rowNumber}").StringValue)
Dim id As Integer = sheet($"A{rowNumber}").IntValue
Dim name As String = sheet($"B{rowNumber}").StringValue
Dim department As String = sheet($"C{rowNumber}").StringValue
Dim salary As Decimal = sheet($"D{rowNumber}").DecimalValue
Console.WriteLine($"{id,4} {name,-20} {department,-15} {salary,10:C}")
totalSalary += salary
employeeCount += 1
rowNumber += 1
End While
Console.WriteLine(vbCrLf & $"Total Employees: {employeeCount}")
Console.WriteLine($"Total Payroll: {totalSalary:C}")
' Write a new record back to the sheet and save
sheet("A10").Value = 1010
sheet("B10").Value = "Jane Doe"
sheet("C10").Value = "Finance"
sheet("D10").Value = 75000
workbook.SaveAs("Employees-Updated.xlsx")
Catch ex As Exception
Console.WriteLine($"Error reading Excel file: {ex.Message}")
End Try
The while loop terminates when column A becomes empty, making the reader adaptive to spreadsheets of any length without requiring a hard-coded row count. Writing values back follows the same cell-address syntax as reading, and SaveAs persists the changes to a new file so the original remains untouched. The try/catch block handles missing files, locked workbooks, and corrupted data -- all scenarios that occur in production environments where input files arrive from external sources.
Output

The how-to guide for creating spreadsheets and the guide for converting XLSX to CSV show how to combine reading and writing in end-to-end workflows. The cell styling guide covers font sizes, colors, and borders when formatted output is a requirement.

How Do You Handle Advanced Reading Scenarios?
Several less-common but important scenarios arise in real projects. Named ranges, worksheet discovery, and CSV loading each have dedicated API support.
Named ranges let you reference data by a logical label rather than a cell address. If the workbook author defined a named range called SalaryTable, you access it directly through GetRangeByName:
var range = workbook.GetRangeByName("SalaryTable");
decimal total = range.Sum();
Console.WriteLine($"Named range total: {total:C}");
var range = workbook.GetRangeByName("SalaryTable");
decimal total = range.Sum();
Console.WriteLine($"Named range total: {total:C}");
Dim range = workbook.GetRangeByName("SalaryTable")
Dim total As Decimal = range.Sum()
Console.WriteLine($"Named range total: {total:C}")
Worksheet discovery enumerates every sheet in the workbook, which is useful when processing files with a variable number of tabs:
foreach (WorkSheet ws in workbook.WorkSheets)
Console.WriteLine($"Sheet: {ws.Name}, Rows: {ws.RowCount}");
foreach (WorkSheet ws in workbook.WorkSheets)
Console.WriteLine($"Sheet: {ws.Name}, Rows: {ws.RowCount}");
For Each ws As WorkSheet In workbook.WorkSheets
Console.WriteLine($"Sheet: {ws.Name}, Rows: {ws.RowCount}")
Next
CSV loading uses a dedicated method optimized for plain-text files, returning a WorkSheet you navigate with the same range API:
WorkBook csv = WorkBook.LoadCSV("data.csv");
WorkSheet first = csv.DefaultWorkSheet;
Console.WriteLine(first["A1"].StringValue);
WorkBook csv = WorkBook.LoadCSV("data.csv");
WorkSheet first = csv.DefaultWorkSheet;
Console.WriteLine(first["A1"].StringValue);
Dim csv As WorkBook = WorkBook.LoadCSV("data.csv")
Dim first As WorkSheet = csv.DefaultWorkSheet
Console.WriteLine(first("A1").StringValue)
These patterns cover the advanced reading scenarios that appear in data migration projects, ETL pipelines, and automated reporting systems. For the complete API surface, see the IronXL object reference.
Additional authoritative references:
- IronXL documentation home
- How to edit Excel formulas
- How to work with math functions
- How to select ranges
- ECMA-376 Open XML specification -- the authoritative standard defining the XLSX format
- Microsoft Open XML SDK documentation -- reference material for the underlying file structure
- NuGet Gallery -- IronXL.Excel -- package page with version history and download statistics
What Are Your Next Steps?
IronXL turns Excel file reading from a dependency-laden Interop challenge into a few lines of straightforward C# code. Loading workbooks, extracting typed values, iterating row by row, running aggregate calculations, and handling edge cases all follow the same consistent API pattern -- without Microsoft Office installed anywhere in your deployment environment.
To move from tutorial to production:
- Start a free trial to unlock the full IronXL feature set and validate it against your own Excel files before committing to a license.
- Browse the complete IronXL documentation for guides on writing, formatting, styling, and converting spreadsheets.
- Explore the object reference and full API to discover every class, method, and property available in the library.
- Review the how-to guide for creating spreadsheets when your workflow requires generating output files as well as reading input files.
- Check the IronXL blog post index for additional tutorials covering real-world scenarios such as report generation, data validation, and multi-sheet consolidation.
Frequently Asked Questions
What is IronXL?
IronXL is a .NET library that allows developers to read, edit, and create Excel files in various formats such as XLSX and XLS without needing Microsoft Office installed.
How do you read Excel files in C# with IronXL?
Call WorkBook.Load with the file path to open the workbook, then access cells using A1 notation and typed properties such as StringValue, IntValue, and DecimalValue.
Why choose IronXL over Microsoft Office Interop for reading Excel files?
IronXL requires no Microsoft Office installation, eliminating COM dependencies and simplifying server-side deployment.
What Excel file formats can IronXL read?
IronXL reads XLSX, XLS, XLSM, CSV, and TSV formats. Format detection is automatic based on file extension.
Can IronXL handle large Excel files efficiently?
Yes, IronXL is optimized for performance and can handle large Excel files, making it suitable for data-intensive applications.
Does IronXL work with .NET 10?
Yes, IronXL supports modern .NET versions including .NET 10, as well as .NET Framework projects.
How do you run aggregate calculations with IronXL?
Use the built-in Sum, Min, Max, and Avg methods on any cell range. These methods skip empty cells automatically.
Can IronXL read password-protected Excel files?
Yes, pass the password as the second argument to WorkBook.Load: WorkBook.Load("file.xlsx", "password").




