Appearance
GrydReports Module
GrydReports is a comprehensive report generation module for .NET applications. It provides on-demand and scheduled report generation in multiple formats (PDF, Excel, CSV) with pluggable templates, data sources, delivery channels, and multi-tenancy support.
✨ Features
| Feature | Description |
|---|---|
| 📄 Multi-Format Output | Generate reports in PDF, Excel, CSV, and HTML formats |
| 🧩 Pluggable Templates | Code-first template definition with type-safe data and parameters |
| 📊 Data Sources | Generic data sources with streaming support for large datasets |
| ⏱️ Sync & Async | On-demand synchronous generation or background async processing |
| 📅 Scheduling (Optional) | Cron-based recurring generation via optional GrydReports.Scheduling.GrydJobs integration |
| 📬 Delivery | Email, webhook, and storage delivery channels |
| 💾 File Storage | Local filesystem with extensible IReportFileStore (S3, Azure Blob) |
| 🏢 Multi-Tenancy | Built-in tenant isolation for templates, executions, and files |
| 🔍 Observability | OpenTelemetry metrics and activity tracing |
| 🔐 Per-Template RBAC | Fine-grained permissions for generate, view, download, and schedule |
| 🎣 Lifecycle Filters | Chain-of-responsibility hooks at every stage of generation |
| ✅ 478 Tests | Comprehensive test coverage across all layers |
📦 Packages
GrydReports follows Clean Architecture with modular packages:
📦 GrydReports
├── GrydReports.Core # Domain entities, abstractions, DTOs
├── GrydReports.Application # CQRS commands, queries, MediatR handlers
├── GrydReports.Infrastructure # EF Core, file storage, delivery (scheduling bridge included)
├── GrydReports.Scheduling.GrydJobs # Optional recurring scheduling integration with GrydJobs
├── GrydReports.Infrastructure.QuestPdf # QuestPDF renderer
├── GrydReports.Infrastructure.ClosedXml # ClosedXML (Excel) renderer
├── GrydReports.Infrastructure.CsvHelper # CsvHelper (CSV) renderer
├── GrydReports.API # REST controllers (15 endpoints)
└── GrydReports (meta-package) # All packages combined🚀 Quick Start
Installation
bash
# Install Reports core stack (without requiring Jobs)
dotnet add package GrydReportsbash
# Core abstractions only
dotnet add package GrydReports.Core
# With specific renderers
dotnet add package GrydReports.Infrastructure.QuestPdf
dotnet add package GrydReports.Infrastructure.ClosedXml
dotnet add package GrydReports.Infrastructure.CsvHelper
# Optional: only if you need recurring schedules with GrydJobs
dotnet add package GrydReports.Scheduling.GrydJobsBasic Setup
csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Register all GrydReports services (Application + Infrastructure + API)
builder.Services.AddGrydReports(
builder.Configuration,
db => db.UseNpgsql(builder.Configuration.GetConnectionString("GrydReports"))
);
// Optional: enable recurring scheduling via GrydJobs
// builder.Services.AddGrydJobs(...);
// builder.Services.AddGrydReportsScheduling();
// Optional: health checks
builder.Services.AddGrydReportsHealthChecks(builder.Configuration);
var app = builder.Build();
// Apply migrations + middleware
app.UseGrydReports();
await app.ApplyGrydReportsMigrationsAsync();
// Optional when using GrydJobs
// await app.ApplyGrydJobsMigrationsAsync();
app.Run();Create Your First Report
csharp
// 1. Define the data model
public class SalesData
{
public string Period { get; set; }
public List<SaleItem> Items { get; set; }
public decimal Total { get; set; }
}
// 2. Implement the data source
public class SalesDataSource : IReportDataSource<SalesData, SalesParameters>
{
private readonly IDbContext _db;
public SalesDataSource(IDbContext db) => _db = db;
public async Task<SalesData> FetchDataAsync(
SalesParameters parameters, CancellationToken ct)
{
var items = await _db.Sales
.Where(s => s.Date >= parameters.FromDate && s.Date <= parameters.ToDate)
.ToListAsync(ct);
return new SalesData
{
Period = $"{parameters.FromDate:MMM yyyy}",
Items = items,
Total = items.Sum(i => i.Amount)
};
}
}
// 3. Define the template
public class MonthlySalesTemplate : IReportTemplate<SalesData>
{
public string TemplateId => "monthly-sales";
public string DisplayName => "Monthly Sales Report";
public string? Description => "Summarizes sales by period";
public IReadOnlyList<ReportFormat> SupportedFormats =>
[ReportFormat.Pdf, ReportFormat.Excel, ReportFormat.Csv];
public ReportMetadata GetMetadata(SalesData data) => new()
{
Title = $"Sales Report - {data.Period}",
Author = "Finance Department"
};
}See Getting Started for the full walkthrough.
🏗️ Architecture
┌────────────────────────────────────────────────┐
│ GrydReports.API │
│ ReportsController SchedulesController │
├────────────────────────────────────────────────┤
│ GrydReports.Application │
│ Commands + Queries (MediatR/CQRS) │
├────────────────────────────────────────────────┤
│ GrydReports.Core │
│ Abstractions │ Entities │ DTOs │ Enums │
├────────────────────────────────────────────────┤
│ GrydReports.Infrastructure │
│ EF Core │ FileStore │ Delivery │ Scheduling │
├──────────┬──────────────┬──────────────────────┤
│ QuestPDF │ ClosedXML │ CsvHelper │
│ (PDF) │ (Excel) │ (CSV) │
└──────────┴──────────────┴──────────────────────┘📋 API Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/reports | List available report templates |
GET | /api/v1/reports/history | Paginated execution history |
GET | /api/v1/reports/{id} | Get execution details |
POST | /api/v1/reports/generate | Generate report synchronously |
POST | /api/v1/reports/generate-async | Queue async generation |
GET | /api/v1/reports/{id}/download | Download generated file |
DELETE | /api/v1/reports/{id} | Delete execution & file |
GET | /api/v1/reports/schedules | List schedules |
GET | /api/v1/reports/schedules/{id} | Get schedule details |
POST | /api/v1/reports/schedules | Create recurring schedule |
PUT | /api/v1/reports/schedules/{id} | Update schedule |
DELETE | /api/v1/reports/schedules/{id} | Delete schedule |
POST | /api/v1/reports/schedules/{id}/trigger | Trigger immediate execution |
POST | /api/v1/reports/schedules/{id}/pause | Pause schedule |
POST | /api/v1/reports/schedules/{id}/resume | Resume schedule |
🔗 Related
- Getting Started — Full setup walkthrough
- Configuration — All configuration options
- Templates — Code-first template guide
- Generation — Sync, async, and batch generation
- Scheduling — Cron-based recurring reports
- Migration (No Jobs) — Remove forced Jobs usage safely
- Delivery — Email, webhook, and storage delivery
- API Reference — Endpoint details and DTOs
- Advanced — Filters, caching, streaming, monitoring