This guide provides proper, step-by-step instructions to create a .NET Core Web API project, integrate RDLC reports, and export them as PDF, Excel, and Word files. Follow each step carefully.
Table of Contents
- Prerequisites
- Step 1: Create the .NET Core Web API Project
- Step 2: Install Required NuGet Packages
- Step 3: Create the Report Model (DTO)
- Step 4: Design the RDLC Report File
- Step 5: Create the Report Service (Interface & Implementation)
- Step 6: Register the Service in Program.cs
- Step 7: Create the API Controller
- Step 8: Run and Test the API
- Troubleshooting Common Errors
- Conclusion
Prerequisites
- .NET Core SDK 5.0 or later (6.0, 7.0, or 8.0 recommended)
- Visual Studio 2022 or later (or VS Code with C# extensions)
- RDLC Report Designer extension for Visual Studio (optional, but helpful for designing reports)
- Basic knowledge of C# and ASP.NET Core
Step 1: Create the .NET Core Web API Project
Open Visual Studio or your terminal and create a new ASP.NET Core Web API project.
Using Visual Studio:
- Go to File → New → Project.
- Select "ASP.NET Core Web API" template.
- Name the project:
RDLCReportAPI. - Choose a location and click Next.
- Select framework: .NET 6.0 (or higher).
- Uncheck "Use controllers" if you want minimal APIs, but for this guide we'll use controllers. Keep "Enable OpenAPI support" unchecked for simplicity.
- Click Create.
Using .NET CLI:
dotnet new webapi -n RDLCReportAPI
cd RDLCReportAPI
Step 2: Install Required NuGet Packages
Open the NuGet Package Manager Console or use the CLI to install the following essential packages:
Install-Package AspNetCore.Reporting -Version 1.0.3
Install-Package System.Drawing.Common -Version 7.0.0
Install-Package System.Text.Encoding.CodePages -Version 7.0.0
Explanation of packages:
- AspNetCore.Reporting: Provides RDLC report rendering and export to PDF, Excel, Word.
- System.Drawing.Common: Required for graphics operations during report rendering.
- System.Text.Encoding.CodePages: Handles encoding for different report formats.
Note: If you get any compatibility issues, use the latest stable versions.
Step 3: Create the Report Model (DTO)
Create a folder named Models and add a class UserReportDto.cs. This will represent the data for our report.
File Path: RDLCReportAPI/Models/UserReportDto.cs
namespace RDLCReportAPI.Models
{
public class UserReportDto
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
}
}
Step 4: Design the RDLC Report File
RDLC reports are XML files with a .rdlc extension. You can create them using Visual Studio or a third-party designer. Follow these steps:
- Right-click the project → Add → New Folder. Name it
Reports. - Right-click the
Reportsfolder → Add → New Item. - Search for "Report" and select "Report (.rdlc)". Name it
UserDetails.rdlc. - If the template is missing, you need to install the Microsoft RDLC Report Designer extension from Visual Studio Marketplace.
Designing the Report:
- Open
UserDetails.rdlc. The designer will appear. - From the Toolbox, drag a Table control onto the report body.
- You'll see three rows: Header, Details, Footer.
- Now we need to create a DataSet. Right-click on the report surface → Report Data → In the Report Data panel, click New → DataSet.
- Name:
UserDataSet - Data source: New → Choose Object → Select
UserReportDtofrom your project. - Click OK.
- Name:
- Now drag fields from the Report Data panel (FirstName, LastName, Email, Phone) into the table's detail row (second row).
- Customize column headers in the header row (first row).
- Adjust widths, fonts, borders as needed.
- Save the file.
Important Setup for RDLC file properties:
- Right-click
UserDetails.rdlc→ Properties. - Set Copy to Output Directory to "Copy if newer" or "Copy always". This ensures the report file is available in the build output.
Step 5: Create the Report Service (Interface & Implementation)
Create a folder Services. Then add an interface and its implementation.
5.1 Create Interface: IReportService.cs
File Path: RDLCReportAPI/Services/IReportService.cs
using System.Threading.Tasks;
namespace RDLCReportAPI.Services
{
public interface IReportService
{
Task<byte[]> GenerateReportAsync(string reportName, string reportType);
}
}
5.2 Create Implementation: ReportService.cs
File Path: RDLCReportAPI/Services/ReportService.cs
using AspNetCore.Reporting;
using RDLCReportAPI.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace RDLCReportAPI.Services
{
public class ReportService : IReportService
{
public ReportService()
{
// Register encoding provider to support various encodings
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Enable unsafe binary formatter serialization (required for Word export)
AppContext.SetSwitch("Switch.System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true);
}
public async Task<byte[]> GenerateReportAsync(string reportName, string reportType)
{
try
{
// 1. Get the path of the RDLC file from the assembly's location
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string assemblyDirectory = Path.GetDirectoryName(assemblyLocation);
string reportPath = Path.Combine(assemblyDirectory, "Reports", $"{reportName}.rdlc");
if (!File.Exists(reportPath))
{
throw new FileNotFoundException($"Report file not found: {reportPath}");
}
// 2. Prepare data for the report (in real scenario, fetch from database)
List<UserReportDto> users = GetUserData();
// 3. Load the RDLC report
using var report = new LocalReport(reportPath);
// 4. Add data source (must match the DataSet name used in RDLC)
report.AddDataSource("UserDataSet", users);
// 5. Determine the render type based on requested format
RenderType renderType = GetRenderType(reportType);
// 6. Render the report to byte array
var result = await report.ExecuteAsync(renderType, 1, null, null);
return result.MainStream;
}
catch (Exception ex)
{
throw new Exception($"Error generating report: {ex.Message}", ex);
}
}
private List<UserReportDto> GetUserData()
{
// Static sample data. Replace with database call in production.
return new List<UserReportDto>
{
new UserReportDto { FirstName = "John", LastName = "Doe", Email = "john.doe@example.com", Phone = "+1-555-123-4567" },
new UserReportDto { FirstName = "Jane", LastName = "Smith", Email = "jane.smith@example.com", Phone = "+1-555-987-6543" },
new UserReportDto { FirstName = "Robert", LastName = "Johnson", Email = "robert.j@example.com", Phone = "+1-555-456-7890" },
new UserReportDto { FirstName = "Emily", LastName = "Davis", Email = "emily.davis@example.com", Phone = "+1-555-321-0987" }
};
}
private RenderType GetRenderType(string reportType)
{
return reportType?.ToLower() switch
{
"pdf" => RenderType.Pdf,
"xls" => RenderType.Excel,
"word" => RenderType.Word,
_ => RenderType.Pdf
};
}
}
}
Step 6: Register the Service in Program.cs
Open Program.cs and add the dependency injection for the report service. Also configure the necessary switches for binary serialization.
File Path: RDLCReportAPI/Program.cs
using RDLCReportAPI.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // optional
// Register the report service as scoped
builder.Services.AddScoped<IReportService, ReportService>();
// Enable unsafe binary formatter serialization (required for Word reports)
AppContext.SetSwitch("Switch.System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 7: Create the API Controller
Add a new controller named ReportController.cs inside the Controllers folder.
File Path: RDLCReportAPI/Controllers/ReportController.cs
using Microsoft.AspNetCore.Mvc;
using RDLCReportAPI.Services;
using System.Threading.Tasks;
namespace RDLCReportAPI.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ReportController : ControllerBase
{
private readonly IReportService _reportService;
public ReportController(IReportService reportService)
{
_reportService = reportService;
}
/// <summary>
/// Generates and downloads a report in the specified format.
/// </summary>
/// <param name="reportName">Name of the report (without extension), e.g., "UserDetails"</param>
/// <param name="format">Format: pdf, xls, or word</param>
[HttpGet]
public async Task<IActionResult> GetReport(string reportName, string format)
{
if (string.IsNullOrEmpty(reportName) || string.IsNullOrEmpty(format))
{
return BadRequest("Report name and format are required.");
}
// Validate format
string[] allowedFormats = { "pdf", "xls", "word" };
if (!allowedFormats.Contains(format.ToLower()))
{
return BadRequest("Invalid format. Allowed formats: pdf, xls, word.");
}
try
{
// Generate report as byte array
byte[] reportBytes = await _reportService.GenerateReportAsync(reportName, format);
// Determine content type and file extension
string contentType = GetContentType(format);
string fileExtension = GetFileExtension(format);
string fileName = $"{reportName}_{System.DateTime.Now:yyyyMMddHHmmss}.{fileExtension}";
// Return the file for download
return File(reportBytes, contentType, fileName);
}
catch (System.Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
private string GetContentType(string format)
{
return format.ToLower() switch
{
"pdf" => "application/pdf",
"xls" => "application/vnd.ms-excel",
"word" => "application/msword",
_ => "application/octet-stream"
};
}
private string GetFileExtension(string format)
{
return format.ToLower() switch
{
"pdf" => "pdf",
"xls" => "xls",
"word" => "doc",
_ => "dat"
};
}
}
}
Step 8: Run and Test the API
Now you can run the application and test the report generation endpoints.
- Press F5 or run
dotnet runfrom the terminal. - The API will start, typically at
https://localhost:5001or a similar port. - Open your browser or Postman and test the following URLs:
// For PDF report:
https://localhost:5001/api/report?reportName=UserDetails&format=pdf
// For Excel report:
https://localhost:5001/api/report?reportName=UserDetails&format=xls
// For Word report:
https://localhost:5001/api/report?reportName=UserDetails&format=word
The browser should automatically download the file in the requested format. The report will contain the sample user data.
Troubleshooting Common Errors
Error 1: "An error occurred during local report processing"
Cause: Usually due to missing encoding provider or binary formatter serialization not enabled.
Solution: Ensure you have called Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); and set AppContext.SetSwitch("Switch.System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true); in your service constructor or Program.cs.
Error 2: "Report file not found"
Cause: The RDLC file is not being copied to the output directory.
Solution: Set the RDLC file's "Copy to Output Directory" property to "Copy always" or "Copy if newer". Also verify the file path in the service matches the folder structure.
Error 3: "Data source name not found in the report"
Cause: The DataSet name used in AddDataSource does not match the DataSet name defined in the RDLC file.
Solution: Open the RDLC file, go to Report Data panel, check the DataSet name. In our example it is "UserDataSet". Use exactly the same name in report.AddDataSource("UserDataSet", users);
Error 4: Word export gives "Binary formatter serialization is disabled"
Solution: This is already handled in the ReportService constructor. Make sure the AppContext switch is set before the report execution.
Error 5: System.Drawing.Common is not supported on Linux
Note: If deploying to Linux, you may need to install libgdiplus. Alternatively, consider using a different reporting library for cross-platform compatibility. For Windows deployment, it works fine.
Conclusion
Congratulations! You have successfully created a .NET Core Web API that generates RDLC reports and exports them to PDF, Excel, and Word formats. This guide provided proper step-by-step instructions including:
- Creating the Web API project.
- Installing necessary NuGet packages.
- Creating a DTO model.
- Designing an RDLC report file.
- Implementing a report service with rendering logic.
- Registering the service and creating an API controller.
- Testing all three export formats.
You can now extend this foundation by connecting to a real database, adding report parameters, customizing the report design with charts and subreports, and securing the API endpoints. This approach saves tremendous development time and provides a robust reporting solution for your applications.
Happy Coding!