EPPlus.Core.Extensions

An extensions library for EPPlus to generate and manipulate Excel files easily in .NET 10.

View the Project on GitHub eraydin/EPPlus.Core.Extensions

EPPlus.Core.Extensions CI

Installation NuGet version

PM> Install-Package EPPlus.Core.Extensions

Dependencies

.NET 10.0EPPlus >= 8.6.1

License Setup

EPPlus 8 requires a license declaration before use. For non-commercial/personal projects:

ExcelPackage.License.SetNonCommercialPersonal("YourName");

For commercial use, see EPPlus licensing.

Features

Examples

Mapping a class to Excel columns

Use [ExcelTableColumn] to map properties to Excel columns by name or index:

public class PersonDto
{
    [ExcelTableColumn("First name")]
    [Required(ErrorMessage = "First name cannot be empty.")]
    [MaxLength(50, ErrorMessage = "First name cannot be more than {1} characters.")]
    public string FirstName { get; set; }

    [ExcelTableColumn(columnName = "Last name", isOptional = true)]
    public string LastName { get; set; }

    [ExcelTableColumn(3)]
    [Range(1900, 2050, ErrorMessage = "Please enter a value bigger than {1}")]
    public int YearBorn { get; set; }

    public decimal NotMapped { get; set; }

    [ExcelTableColumn(isOptional = true)]
    public decimal OptionalColumn1 { get; set; }

    [ExcelTableColumn(columnIndex = 999, isOptional = true)]
    public decimal OptionalColumn2 { get; set; }
}

Reading Excel data into a list

// From the first worksheet:
List<PersonDto> firstWorksheetPersons = excelPackage.ToList<PersonDto>(c => c.SkipCastingErrors());

// From a named worksheet:
List<PersonDto> namedWorksheetPersons = excelPackage.GetWorksheet("Persons").ToList<PersonDto>();

// Or read a named worksheet directly from the package:
List<PersonDto> directNamedWorksheetPersons = excelPackage.ToListFromWorksheet<PersonDto>("Persons");

Reading without exceptions

Use Read<T> when a workbook is user input and all usable rows and errors should be returned together:

ExcelReadResult<PersonDto> result = excelPackage.Read<PersonDto>("Persons");

foreach (PersonDto person in result.Items)
{
    // Rows with invalid cells are retained and may be partially populated.
}

foreach (ExcelReadError error in result.Errors)
{
    Console.WriteLine($"{error.Kind}: {error.Context.CellAddress} - {error.Message}");
}

ToList<T> and AsEnumerable<T> keep their existing throw/skip behavior. Read<T> captures missing-column mappings, casting failures, and data annotation validation errors.

Safe workbook lookups

if (excelPackage.TryGetWorksheet("Persons", out ExcelWorksheet worksheet))
{
    List<PersonDto> persons = worksheet.ToList<PersonDto>();
}

if (excelPackage.TryGetTable("PeopleTable", out ExcelTable table))
{
    ExcelReadResult<PersonDto> result = table.Read<PersonDto>();
}

Reading when the header row is not on row 1

Use WithHeaderRowIndex when your sheet has title rows above the actual column headers:

// Header is on row 3 — rows 1 and 2 are skipped
List<PersonDto> persons = worksheet.ToList<PersonDto>(c => c.WithHeaderRowIndex(3));

Mapping nested class properties as flat columns

Use [ExcelNestedColumn] on a complex-type property to have its sub-properties mapped as flat columns:

public class Order
{
    [ExcelTableColumn]
    public string OrderId { get; set; }

    [ExcelNestedColumn]
    public Address ShippingAddress { get; set; }
}

public class Address
{
    [ExcelTableColumn]
    public string Street { get; set; }

    [ExcelTableColumn]
    public string City { get; set; }
}

// A worksheet with columns OrderId | Street | City maps correctly:
List<Order> orders = worksheet.ToList<Order>();
// orders[0].ShippingAddress.Street => "123 Main St"

Writing a list to an Excel package

// Convert to ExcelPackage
ExcelPackage excelPackage = persons.ToExcelPackage();

// Convert to byte array
byte[] xlsx = persons.ToXlsx();

// Specify the worksheet name
byte[] namedXlsx = persons.ToXlsx("Persons", addHeaderRow: true);

// Start the fluent builder with the default worksheet name (typeof(T).Name)
WorksheetWrapper<PersonDto> worksheet = persons.ToWorksheet();

// Fluent multi-worksheet builder
List<PersonDto> pre50  = persons.Where(x => x.YearBorn < 1950).ToList();
List<PersonDto> post50 = persons.Where(x => x.YearBorn >= 1950).ToList();

ExcelPackage excelPackage = pre50.ToWorksheet("< 1950")
    .WithConfiguration(c => c.WithColumnConfiguration(x => x.AutoFit()))
    .WithColumn(x => x.FirstName, "First Name")
    .WithColumn(x => x.LastName, "Last Name")
    .WithColumn(x => x.YearBorn, "Year of Birth")
    .WithTitle("< 1950")
    .NextWorksheet(post50, "> 1950")
    .WithColumn(x => x.LastName, "Last Name")
    .WithColumn(x => x.YearBorn, "Year of Birth")
    .WithTitle("> 1950")
    .ToExcelPackage();

Generating an Excel template from a class

Mark a class with [ExcelWorksheet] to generate a structured template:

[ExcelWorksheet("Stocks")]
public class StocksDto
{
    [ExcelTableColumn("SKU")]
    public string Barcode { get; set; }

    [ExcelTableColumn]
    public int Quantity { get; set; }
}

// Generate as ExcelPackage
ExcelPackage excelPackage = Assembly.GetExecutingAssembly().GenerateExcelPackage(nameof(StocksDto));

// Generate as ExcelWorksheet inside an existing package
using var excelPackage = new ExcelPackage();
ExcelWorksheet worksheet = excelPackage.GenerateWorksheet(Assembly.GetExecutingAssembly(), nameof(StocksDto));