Skip to content

Report Generation

GrydReports supports three generation modes: synchronous (on-demand), asynchronous (background), and batch (multiple reports). All modes follow the same pipeline: validate → fetch data → render → store → deliver.

Generation Pipeline

Request → Validate Parameters → Fetch Data → Render → Store File → Deliver (optional)
              │                     │            │          │            │
          IReportFilter      IReportDataSource  IReportRenderer  IReportFileStore  IReportDelivery
         (OnBefore...)        (FetchDataAsync)  (RenderAsync)   (SaveAsync)       (DeliverAsync)

Synchronous Generation

Returns the generated file immediately. Best for small-to-medium reports:

Via API

bash
POST /api/v1/reports/generate
Content-Type: application/json

{
  "templateId": "monthly-sales",
  "format": 1,
  "parameters": {
    "month": 1,
    "year": 2026
  },
  "requestedBy": "user@company.com"
}

Response 201 Created:

json
{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "templateId": "monthly-sales",
  "reportName": "Sales Report - January 2026",
  "format": 1,
  "status": 3,
  "fileName": "monthly-sales-2026-01.pdf",
  "fileSizeBytes": 245760,
  "downloadUrl": "/api/v1/reports/f47ac10b-.../download",
  "generationDuration": "00:00:02.341",
  "createdAt": "2026-02-19T10:30:00Z"
}

Via MediatR

csharp
var result = await mediator.Send(new GenerateReportCommand(
    TemplateId: "monthly-sales",
    Format: ReportFormat.Pdf,
    Parameters: new Dictionary<string, object?>
    {
        ["month"] = 1,
        ["year"] = 2026
    },
    RequestedBy: "user@company.com"
));

if (result.IsSuccess)
{
    var report = result.Data!;
    Console.WriteLine($"Generated: {report.FileName} ({report.FileSizeBytes} bytes)");
}
else
{
    Console.WriteLine($"Error: {result.ErrorMessage}");
}

Asynchronous Generation

Queues the report for background processing. Returns immediately with a tracking ID. Ideal for large reports or when delivery is configured:

Via API

bash
POST /api/v1/reports/generate-async
Content-Type: application/json

{
  "templateId": "annual-financial",
  "format": 2,
  "parameters": {
    "year": 2025
  },
  "delivery": {
    "method": 2,
    "recipients": ["cfo@company.com", "finance@company.com"],
    "emailSubject": "Annual Financial Report 2025",
    "emailBody": "Please find the attached annual financial report."
  },
  "requestedBy": "admin@company.com"
}

Response 202 Accepted:

json
{
  "id": "a1b2c3d4-...",
  "templateId": "annual-financial",
  "status": 1,
  "createdAt": "2026-02-19T10:35:00Z"
}

Via MediatR

csharp
var result = await mediator.Send(new GenerateReportAsyncCommand(
    TemplateId: "annual-financial",
    Format: ReportFormat.Excel,
    Parameters: new Dictionary<string, object?> { ["year"] = 2025 },
    Delivery: new DeliveryOptions
    {
        Method = DeliveryMethod.Email,
        Recipients = ["cfo@company.com"]
    },
    RequestedBy: "admin@company.com"
));

// Returns immediately — report is generated in background
var trackingId = result.Data!.Id;

Polling for Status

bash
GET /api/v1/reports/{trackingId}

Status transitions: Queued (1)Generating (2)Completed (3) or Failed (4)Delivered (5)

IReportGenerator

The central orchestrator that wires everything together:

csharp
public interface IReportGenerator
{
    // Generate and return the output stream
    Task<Result<ReportOutput>> GenerateAsync<TData, TParams>(
        string templateId, TParams parameters, ReportFormat format,
        CancellationToken ct = default);

    // Generate + store in file storage + persist execution metadata
    Task<Result<ReportResultDto>> GenerateAndStoreAsync<TData, TParams>(
        string templateId, TParams parameters, ReportFormat format,
        CancellationToken ct = default);

    // Generate + store + deliver via configured channel
    Task<Result<ReportResultDto>> GenerateAndDeliverAsync<TData, TParams>(
        string templateId, TParams parameters, ReportFormat format,
        DeliveryOptions delivery, CancellationToken ct = default);

    // List all registered report templates
    IReadOnlyList<ReportDefinitionDto> GetAvailableReports();
}

Download

Retrieve a previously generated report file:

bash
GET /api/v1/reports/{id}/download

Returns a FileStreamResult with proper Content-Type and Content-Disposition headers.

Execution History

Query past report executions with filters:

bash
GET /api/v1/reports/history?templateId=monthly-sales&status=3&format=1&pageNumber=1&pageSize=20

Query Parameters:

ParameterTypeDescription
templateIdstring?Filter by template
statusint?Filter by status (1=Queued, 2=Generating, 3=Completed, 4=Failed, 5=Delivered, 6=Cancelled)
formatint?Filter by format (1=PDF, 2=Excel, 3=CSV, 4=HTML)
fromDateDateTime?Start of date range
toDateDateTime?End of date range
pageNumberintPage number (default: 1)
pageSizeintPage size (default: 20)

Delete

Soft-delete a report execution and its associated file:

bash
DELETE /api/v1/reports/{id}

Returns 204 No Content on success.

Size Limits

Configure automatic async fallback for large reports:

csharp
options.MaxSyncGenerationSize = 50 * 1024 * 1024; // 50 MB max for sync
options.ForceAsyncAboveBytes = 10 * 1024 * 1024;   // Force async above 10 MB
options.SyncGenerationTimeout = TimeSpan.FromMinutes(2);

ReportOutput

The output object returned from generation:

csharp
public sealed class ReportOutput : IDisposable
{
    public Stream Content { get; init; }         // File content stream
    public string FileName { get; init; }        // e.g. "report.pdf"
    public string ContentType { get; init; }     // e.g. "application/pdf"
    public ReportFormat Format { get; init; }    // Pdf, Excel, Csv, Html
    public long FileSizeBytes { get; init; }     // Size in bytes
    public ReportMetadata Metadata { get; init; } // Title, Author, etc.
    public TimeSpan GenerationDuration { get; init; }

    public void Dispose() => Content?.Dispose();
}

Resource Management

ReportOutput implements IDisposable. Always dispose when using the output directly:

csharp
using var output = (await generator.GenerateAsync<SalesData, SalesParams>(
    "sales", parameters, ReportFormat.Pdf)).Data!;
// Use output.Content stream...

Released under the MIT License.