A clean, production-ready report engine for .NET Framework 4.5.2+ that converts SQL result data into business-friendly Excel, HTML, and CSV reports with human-readable formatting. Features: - Fluent API for defining reports - Support for DataTable and List<Dictionary<string, object>> - Multiple output formats (Excel, HTML, CSV) - Business logic layer for grouping and calculations - Currency formatting with custom rules - "Explain Like I'm Five" report summaries - Clean separation of concerns - Enterprise-ready code quality
$ dotnet add package HumanReadableReportsA clean, production-ready report engine for .NET Framework 4.5.2+ that converts SQL result data into business-friendly Excel, HTML, and CSV reports with minimal code.
Enterprise developers often need to:
Traditional approach: Write complex rendering code, handle formatting logic, manage EPPlus manually, repeat for each report.
With HumanReadableReports: Define your report in 5 lines of readable code. The library handles all the complexity.
Install-Package HumanReadableReports
using HumanReadableReports.Definition;
using HumanReadableReports.Models;
// Get your data
DataTable data = GetSalesDataFromDatabase();
// Define and generate report
var report = Report
.From(data)
.WithTitle("Monthly Sales Report")
.Column("TransactionDate", "Date").AsDate("dd MMM yyyy")
.Column("Product")
.Column("Amount").AsCurrency("IDR")
.GroupBy("TransactionDate", GroupingType.Month)
.Total("Amount");
// Generate Excel file
byte[] excelFile = report.ToExcel();
File.WriteAllBytes("SalesReport.xlsx", excelFile);
That's it! You now have a professional Excel report with:
The library supports two data sources:
DataTable data = GetDataFromStoredProcedure();
var report = Report.From(data);
var data = new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "Date", new DateTime(2024, 10, 15) },
{ "Customer", "PT Maju Jaya" },
{ "Amount", 25000000m }
}
};
var report = Report.From(data);
// Basic column
.Column("ColumnName")
// Column with display name
.Column("ColumnName", "Display Name")
// Date column with format
.Column("OrderDate").AsDate("dd MMM yyyy")
// Currency column
.Column("Amount").AsCurrency("IDR") // Rp 1.000.000,00
.Column("Price").AsCurrency("USD") // $1,000.00
.Column("Total").AsCurrency("EUR") // €1,000.00
// Number column
.Column("Quantity").AsNumber() // 1,234.56
// Percentage column
.Column("Growth").AsPercentage() // 12.34%
// Group by month
.GroupBy("TransactionDate", GroupingType.Month)
// Group by year
.GroupBy("TransactionDate", GroupingType.Year)
// Group by day
.GroupBy("TransactionDate", GroupingType.Day)
// Group by any value (e.g., Category, Department)
.GroupBy("Category", GroupingType.Value)
// No grouping (default)
// Just don't call GroupBy()
// Add totals for specific columns
.Total("Amount")
.Total("Quantity")
.Total("Revenue")
The library will automatically:
// Excel (XLSX)
byte[] excelFile = report.ToExcel();
// HTML (standalone HTML file)
byte[] htmlFile = report.ToHtml();
// CSV
byte[] csvFile = report.ToCsv();
string explanation = report.ExplainLikeImFive();
Console.WriteLine(explanation);
Output:
📊 Report Summary
This report shows 156 records organized into 12 groups.
💰 Total Amount: Rp 1.250.000.000,00
🏆 Highest group: December 2024 with Rp 150.000.000,00
📈 Trend: Growing by 15.3% from start to end.
✅ All calculations have been verified and formatted for easy reading.
using System.Web.Mvc;
using HumanReadableReports.Definition;
public class ReportController : Controller
{
public ActionResult DownloadSalesReport(string format = "excel")
{
// Get data from database
var data = GetSalesData();
// Build report
var report = Report
.From(data)
.WithTitle("Sales Report")
.Column("Date").AsDate("dd MMM yyyy")
.Column("Product")
.Column("Amount").AsCurrency("IDR")
.GroupBy("Date", GroupingType.Month)
.Total("Amount");
// Render based on requested format
byte[] fileContent;
string contentType;
string fileName;
switch (format.ToLower())
{
case "html":
fileContent = report.ToHtml();
contentType = "text/html";
fileName = "SalesReport.html";
break;
case "csv":
fileContent = report.ToCsv();
contentType = "text/csv";
fileName = "SalesReport.csv";
break;
default:
fileContent = report.ToExcel();
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
fileName = "SalesReport.xlsx";
break;
}
return File(fileContent, contentType, fileName);
}
}
public byte[] GenerateFinancialReport(DateTime startDate, DateTime endDate)
{
DataTable data;
using (var conn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("sp_GetFinancialData", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@StartDate", startDate);
cmd.Parameters.AddWithValue("@EndDate", endDate);
var adapter = new SqlDataAdapter(cmd);
data = new DataTable();
adapter.Fill(data);
}
}
var report = Report
.From(data)
.WithTitle($"Financial Report {startDate:MMM yyyy} - {endDate:MMM yyyy}")
.Column("TransactionDate", "Date").AsDate("dd MMM yyyy")
.Column("Description")
.Column("Debit").AsCurrency("IDR")
.Column("Credit").AsCurrency("IDR")
.Column("Balance").AsCurrency("IDR")
.GroupBy("TransactionDate", GroupingType.Month)
.Total("Debit")
.Total("Credit");
return report.ToExcel();
}
The library follows clean architecture principles:
HumanReadableReports/
├── Core/ # Interfaces
│ ├── IReportDefinition
│ ├── IReportProcessor
│ └── IReportRenderer
├── Definition/ # Fluent API
│ ├── Report.cs
│ └── ReportDefinition.cs
├── Processing/ # Business Logic
│ ├── ReportProcessor.cs
│ └── GroupingEngine.cs
├── Rendering/ # Output Generation
│ ├── ExcelRenderer.cs
│ ├── HtmlRenderer.cs
│ └── CsvRenderer.cs
├── Models/ # Data Models
│ ├── ProcessedReport.cs
│ ├── ReportGroup.cs
│ └── ColumnDefinition.cs
└── Explanation/ # Human Explanations
└── ReportExplainer.cs
The library is designed to be easily testable:
[TestClass]
public class ReportProcessorTests
{
[TestMethod]
public void Should_Calculate_Subtotals_Correctly()
{
// Arrange
var data = CreateTestData();
var definition = CreateTestDefinition(data);
var processor = new ReportProcessor();
// Act
var result = processor.Process(definition);
// Assert
Assert.AreEqual(2, result.Groups.Count);
Assert.AreEqual(150000m, result.Groups[0].Subtotals["Amount"]);
Assert.AreEqual(250000m, result.Groups[1].Subtotals["Amount"]);
}
}
That's it! Minimal dependencies keep the package small and maintainable.
The library includes built-in formatters for IDR, USD, EUR, and GBP. To add more:
// Fork the library and modify ColumnDefinition.FormatCurrency()
case "JPY":
return string.Format("¥{0:N0}", amount); // Japanese Yen (no decimals)
Want to add PDF output? Implement IReportRenderer:
public class PdfRenderer : IReportRenderer
{
public byte[] Render(ProcessedReport report)
{
// Use iTextSharp or similar
// Process report.Groups, report.Columns, etc.
return pdfBytes;
}
public string GetContentType() => "application/pdf";
public string GetFileExtension() => ".pdf";
}
We welcome contributions! Please follow these guidelines:
This project is licensed under the MIT License - see the LICENSE.txt file for details.
A: Not currently. This library targets .NET Framework 4.5.2+. A .NET Standard version is planned for the future.
A: Yes! Convert your query results to DataTable or List:
var data = context.Orders.ToList().ConvertToDataTable();
var report = Report.From(data);
A: The library loads all data into memory for processing. For datasets with 100k+ rows, consider:
A: The library uses professional defaults. For custom styling, you can modify ExcelRenderer.cs in your fork.
A: Yes. Each report generation creates new instances, so you can safely generate multiple reports concurrently.
If this library helps you, please:
Built with ❤️ for enterprise .NET developers who value clean, readable code.