Building Data Export and Reporting Systems in ASP.NET Core: Excel, PDF, and CSV Generation
A practical guide to generating Excel, PDF, and CSV reports in ASP.NET Core applications
Exporting data is a core requirement in many applications, whether it’s generating reports for business users, downloading datasets, or sharing insights externally. In this guide, we’ll walk through how to build reliable data export systems in ASP.NET Core using Excel, PDF, and CSV generation techniques that work in real-world applications.
Most applications eventually need reporting. Users want to download data, finance teams want structured reports, and operations teams need exports they can analyze offline.
At first, it seems simple. Just take some data and convert it into a file. But in production systems, exporting data introduces real challenges. Large datasets can impact performance. Formatting matters. File generation can block requests. And different formats require different handling.
ASP.NET Core gives you the flexibility to build clean, scalable export systems. When combined with the right libraries, you can generate professional reports without adding unnecessary complexity.
In this article, we’ll focus on three common formats: Excel, PDF, and CSV. We’ll look at how to generate each, when to use them, and how to structure your code so it scales as your application grows.
Why Data Export Matters
Data export is more than just a feature. It’s part of how users interact with your system.
Business users often rely on exports to:
Analyze data in Excel
Share reports with stakeholders
Archive records
Integrate with other systems
In many cases, exporting data becomes just as important as displaying it in the UI.
If you’re already working with large datasets or background processing, this ties closely to how bulk data workflows are handled. We explored similar patterns in our guide on batch processing in ASP.NET Core.
Designing a Simple Export Architecture
Before jumping into libraries, it helps to structure your export logic properly.
A clean approach is:
Controller handles the request
Service prepares the data
Export service generates the file
This keeps your application maintainable.
Example:
[HttpGet(”export”)]
public async Task<IActionResult> Export()
{
var data = await _reportService.GetDataAsync();
var fileBytes = _exportService.GenerateCsv(data);
return File(fileBytes, “text/csv”, “report.csv”);
}This pattern works for all formats.
Generating CSV Files with CsvHelper
CSV is the simplest format and often the most efficient.
It’s lightweight, fast, and easy to generate. It’s also widely supported.
We’ll use CsvHelper, a popular library for handling CSV data in .NET.
Install:
dotnet add package CsvHelperExample:
public byte[] GenerateCsv<T>(IEnumerable<T> data)
{
using var memoryStream = new MemoryStream();
using var writer = new StreamWriter(memoryStream);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.WriteRecords(data);
writer.Flush();
return memoryStream.ToArray();
}This generates a CSV file directly in memory.
CSV works best when:
You need raw data export
Formatting is not important
Performance matters
Generating Excel Files with EPPlus
Excel is more powerful than CSV because it supports formatting, formulas, and multiple sheets.
We’ll use EPPlus.
Install:
dotnet add package EPPlusExample:
public byte[] GenerateExcel(List<Product> products)
{
using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add(”Products”);
worksheet.Cells[1, 1].Value = “Id”;
worksheet.Cells[1, 2].Value = “Name”;
worksheet.Cells[1, 3].Value = “Price”;
for (int i = 0; i < products.Count; i++)
{
worksheet.Cells[i + 2, 1].Value = products[i].Id;
worksheet.Cells[i + 2, 2].Value = products[i].Name;
worksheet.Cells[i + 2, 3].Value = products[i].Price;
}
return package.GetAsByteArray();
}Excel is ideal when:
Users need structured reports
Formatting matters
Data needs to be analyzed
For large exports, you should avoid loading everything into memory at once. This is where streaming or batching becomes important, similar to techniques discussed in our file upload and processing guide.
Generating PDFs with QuestPDF
PDFs are best for presentation-ready reports.
We’ll use QuestPDF, which is simple and modern.
Install:
dotnet add package QuestPDFExample:
public byte[] GeneratePdf(List<Product> products)
{
return Document.Create(container =>
{
container.Page(page =>
{
page.Content().Column(column =>
{
column.Item().Text(”Product Report”).FontSize(20);
foreach (var product in products)
{
column.Item().Text($”{product.Name} - {product.Price}”);
}
});
});
}).GeneratePdf();
}PDF is best when:
You need formatted reports
Documents are shared externally
Layout matters
Handling Large Exports
Large exports can slow down your application if handled incorrectly.
Instead of generating files during the request, you can:
Queue the export job
Process it in the background
Notify the user when it’s ready
This is especially important for large datasets.
You can use background services or queues, similar to patterns used in Azure Service Bus messaging systems.
Streaming File Downloads
For large files, avoid loading everything into memory.
Instead, stream the response:
return File(stream, “application/octet-stream”, “report.xlsx”);Streaming improves performance and reduces memory usage.
Choosing the Right Format
Each format serves a different purpose.
CSV is best for raw data.
Excel is best for analysis.
PDF is best for presentation.
In many applications, you’ll support all three.
Real-World Example: Sales Report Export
Imagine a dashboard where users can export sales data.
The workflow might look like this:
The user selects a date range.
The system retrieves data.
The user chooses a format.
The system generates the file.
For small datasets, this can happen instantly.
For large datasets, the export runs in the background and the user downloads it later.
This keeps the application responsive.
Performance Considerations
Exporting data impacts performance.
Keep in mind:
Avoid large in-memory operations
Use async calls
Consider background processing
Monitor export usage
Performance tuning plays a big role here. If you want to go deeper, see our performance tuning guide.
Closing Thoughts
Data export systems are a core part of modern applications. They allow users to work with data outside your system and provide real value.
ASP.NET Core makes it easy to build these systems, but the key is structuring them properly. By separating concerns, using the right libraries, and handling large datasets carefully, you can build export features that scale.
Start simple with CSV, expand into Excel for richer reports, and use PDF when presentation matters.
Join The Community
Enjoyed this article? Subscribe to ASP Today for practical ASP.NET Core insights. Join our Substack Chat to connect with developers building real-world applications.


