Saltar al pie de página
USANDO IRONPDF

Cómo crear archivos PDF en .NET usando IronPDF (Tutorial de C#)

El problema con los informes recurrentes manuales

Inicio de IronPDF Cada organización tiene una versión del mismo ritual del lunes por la mañana: un analista abre la base de datos de informes, ejecuta las mismas consultas que ejecutó la semana pasada, pega los números en una plantilla de Excel, formatea las tablas, las exporta a formato PDF y las envía por correo electrónico a la lista de distribución de liderazgo, esperando que nada falle y que el archivo salga antes del stand-up de las 9 AM. Cuando funciona, es un impuesto invisible en la mañana de alguien. Cuando no funciona, como con datos obsoletos dejados en una pestaña, un error de fórmula en la fila de resumen, un informe que llega a las 10:30 en lugar de a las 7:00, es visible exactamente para el público incorrecto.

Los herramientas BI existentes ofrecen entrega programada, pero generalmente como capturas de pantalla de tableros o enlaces incrustados que requieren un inicio de sesión. Un VP de ventas durante su trayecto no quiere autenticarse en una herramienta BI en su teléfono, quiere un informe PDF que pueda leer en su tableta, reenviar a un prospecto o imprimir antes de una reunión de directorio. Las suscripciones programadas de SSRS producen documentos PDF autónomos pero la salida parece software empresarial de 2010, y actualizar la plantilla requiere navegar un diseñador de informes en el que la mayoría de los equipos .NET actuales ya no tienen experiencia.

Construir una tubería personalizada desde cero es la respuesta arquitectónica correcta, pero significa conectar un programador, una capa de consulta, un motor de plantillas y un remitente de correos, cada uno un posible punto de falla que alerta a alguien a las 6 AM cuando el resumen semanal de P&L del CFO no llega.

La solución: una tubería programada con IronPDF como el paso de renderización

IronPDF es una biblioteca PDF de .NET que actúa como el motor de renderizado dentro de un trabajo .NET en segundo plano programado que ejecuta consultas SQL contra la base de datos de informes, mapea los resultados en una plantilla HTML y CSS, y envía el PDF terminado a una lista de distribución en un cronograma de cron, sin pasos manuales, sin analista requerido.

Hangfire, Quartz.NET, o un BackgroundService disparan el trabajo. ADO.NET o Dapper extraen los números. La plantilla HTML los convierte en un informe de marca. ChromePdfRenderer produces the PDF. El correo electrónico se envía antes de que alguien abra su portátil. Sin capturas de pantalla de herramientas BI, sin definiciones de informes SSRS, sin tarifa de API por informe. La renderización se ejecuta dentro de la aplicación .NET existente, un paquete NuGet, sin procesos externos.

Cómo funciona en la práctica

1. Cronograma de Cron dispara el trabajo

Cada informe tiene su propio cronograma: la instantánea de la tubería de ventas se dispara diariamente a las 6:30 AM, el resumen de P&L se dispara el lunes a las 6:00 AM, el tablero ejecutivo mensual se dispara el primero. El trabajo se registra con su expresión cron en Hangfire o Quartz.NET al iniciar la aplicación, o se implementa como un BackgroundService cronometrado para implementaciones más simples.

Cuando se dispara el trabajo, abre una conexión a la base de datos de informes y ejecuta las consultas SQL relevantes o procedimientos almacenados para el período, cifras de ingresos y gastos, métricas KPI, conteos de etapas de tubería, tasas de conversión o estadísticas de cumplimiento dependiendo del tipo de informe.

2. Los resultados de consultas alimentan la plantilla HTML

Los resultados de la consulta se mapean en un modelo o diccionario y se inyectan en la plantilla HTML. La plantilla define todo sobre la apariencia del informe: el encabezado de marca con el logotipo de la empresa, una etiqueta de período ("Semana del 27 de marzo de 2025"), tarjetas métricas de resumen en la parte superior, una o más tablas de datos con colores de fila alternos, y un pie de página con la marca de tiempo de generación y el aviso de confidencialidad. Este código de ejemplo de IronPDF creará archivos PDF que demuestran exactamente esto.

using Dapper;
using IronPdf;
using System.Data.SqlClient;

var reportData = new List<dynamic>();

using (var conn = new SqlConnection(_connectionString))
{
    reportData = (await conn.QueryAsync(@"
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        new { Start = weekStart, End = weekStart.AddDays(7) })).ToList();
}

var tableRows = string.Concat(reportData.Select(r =>
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"));
string html = (await File.ReadAllTextAsync("Templates/sales-weekly.html"))
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}")
    .Replace("{{TotalRevenue}}", reportData.Sum(r => (decimal)r.Revenue).ToString("C"))
    .Replace("{{TableRows}}", tableRows);

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

PdfDocument report = renderer.RenderHtmlAsPdf(html);
using Dapper;
using IronPdf;
using System.Data.SqlClient;

var reportData = new List<dynamic>();

using (var conn = new SqlConnection(_connectionString))
{
    reportData = (await conn.QueryAsync(@"
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        new { Start = weekStart, End = weekStart.AddDays(7) })).ToList();
}

var tableRows = string.Concat(reportData.Select(r =>
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"));
string html = (await File.ReadAllTextAsync("Templates/sales-weekly.html"))
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}")
    .Replace("{{TotalRevenue}}", reportData.Sum(r => (decimal)r.Revenue).ToString("C"))
    .Replace("{{TableRows}}", tableRows);

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

PdfDocument report = renderer.RenderHtmlAsPdf(html);
Imports Dapper
Imports IronPdf
Imports System.Data.SqlClient

Dim reportData As New List(Of Object)()

Using conn As New SqlConnection(_connectionString)
    reportData = (Await conn.QueryAsync("
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        New With {.Start = weekStart, .End = weekStart.AddDays(7)})).ToList()
End Using

Dim tableRows = String.Concat(reportData.Select(Function(r) 
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"))

Dim html As String = (Await File.ReadAllTextAsync("Templates/sales-weekly.html")) _
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}") _
    .Replace("{{TotalRevenue}}", reportData.Sum(Function(r) CType(r.Revenue, Decimal)).ToString("C")) _
    .Replace("{{TableRows}}", tableRows)

Dim renderer As New ChromePdfRenderer()

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter

renderer.RenderingOptions.MarginTop = 20

renderer.RenderingOptions.MarginBottom = 20

Dim report As PdfDocument = renderer.RenderHtmlAsPdf(html)
$vbLabelText   $csharpLabel

Archivo PDF generado

Ejemplo de PDF generado por IronPDF

3. Documento PDF enviado y archivado

using System.Net.Mail;
using System.IO;

var recipients = await _configService.GetDistributionListAsync("sales-weekly");

string subject = $"Weekly Sales Report — {weekStart:MMM d, yyyy}";

using var stream = new MemoryStream(report.BinaryData);

using var attachment = new Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf");

var message = new MailMessage
{
    From = new MailAddress("reports@yourcompany.com"),
    Subject = subject,
    Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
};

foreach (var recipient in recipients)
    message.To.Add(recipient);

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Archive to blob storage for historical access

string archivePath = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf";

report.SaveAs(archivePath);
using System.Net.Mail;
using System.IO;

var recipients = await _configService.GetDistributionListAsync("sales-weekly");

string subject = $"Weekly Sales Report — {weekStart:MMM d, yyyy}";

using var stream = new MemoryStream(report.BinaryData);

using var attachment = new Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf");

var message = new MailMessage
{
    From = new MailAddress("reports@yourcompany.com"),
    Subject = subject,
    Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
};

foreach (var recipient in recipients)
    message.To.Add(recipient);

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Archive to blob storage for historical access

string archivePath = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf";

report.SaveAs(archivePath);
Imports System.Net.Mail
Imports System.IO

Dim recipients = Await _configService.GetDistributionListAsync("sales-weekly")

Dim subject As String = $"Weekly Sales Report — {weekStart:MMM d, yyyy}"

Using stream As New MemoryStream(report.BinaryData)
    Using attachment As New Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf")
        Dim message As New MailMessage With {
            .From = New MailAddress("reports@yourcompany.com"),
            .Subject = subject,
            .Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
        }

        For Each recipient In recipients
            message.To.Add(recipient)
        Next

        message.Attachments.Add(attachment)

        Using smtp As New SmtpClient("smtp.yourprovider.com")
            Await smtp.SendMailAsync(message)
        End Using
    End Using
End Using

' Archive to blob storage for historical access

Dim archivePath As String = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf"

report.SaveAs(archivePath)
$vbLabelText   $csharpLabel

Correo electrónico de ejemplo con PDF adjunto

Scheduled Pdf Reports Email 3 related to Correo electrónico de ejemplo con PDF adjunto Las listas de distribución se sacan de una tabla de configuración en lugar de codificarse, por lo que agregar un nuevo destinatario al informe semanal de ventas no requiere un cambio de código o una reimplementación. Cada PDF archivado se indexa por nombre de informe y fecha, construyendo una biblioteca de informes pasados accesible desde una unidad compartida o portal interno.

El trabajo registra su estado de ejecución — duración de la consulta, tiempo de renderización, resultado del envío de correo electrónico y cualquier error — en el registro estructurado. Se activa una alerta si el trabajo no se completa, por lo que el primer indicio de un problema no es un ejecutivo preguntando por qué su informe no llegó.

Beneficios reales

Cero esfuerzo manual. Los informes se generan y se entregan por sí mismos según el cronograma. Ningún analista ejecuta consultas el lunes por la mañana, nadie da formato a una tabla, nadie recuerda presionar enviar. El proceso es invisible cuando funciona y alertable cuando no funciona.

Puntualidad. El informe llega a las bandejas de entrada antes de que empiece el día laboral. Un CFO que revise el correo electrónico antes de llegar a la oficina tiene los números del P&L antes de que cualquier reunión los requiera, no después de que un analista termine su café.

Legibilidad sin conexión. Un PDF autónomo que los ejecutivos pueden leer en un avión, imprimir para una reunión de directorio, o reenviar sin requerir que el destinatario inicie sesión en ningún sistema. Sin URL de tablero, sin muro de autenticación, sin "este enlace solo funciona en la red corporativa".

Consistencia de marca. Cada informe producido por la tubería utiliza la misma plantilla HTML y CSS aprobada, la misma colocación de logotipo, tipografía, estilo de tabla y lenguaje de pie de página en cada informe semanal, diario y mensual que distribuye la organización.

Archivo histórico. Cada PDF generado se almacena por fecha en una ruta estructurada. Seis meses de informes de ventas semanales están a solo un paso en una carpeta para una revisión de tendencias o una solicitud de auditoría, sin necesidad de consulta de herramienta BI.

Sin costos por informe. La renderización se ejecuta en proceso dentro de la aplicación .NET existente. No se requiere un asiento premium de herramienta BI para la entrega programada ni conteo de API de informes que escale con el volumen de informes o el tamaño de las listas de distribución.

Cierre

Un informe recurrente que llega de manera confiable antes de que comience el día laboral es un pequeño detalle operativo que afecta cómo se toman las decisiones. La dirección que tiene los números antes del standup de la mañana lleva una reunión diferente a una dirección que espera hasta que un analista termine de formatear una hoja de cálculo.

La tubería para llegar allí — programador, consulta, plantilla, renderizado, correo electrónico — cabe cómodamente dentro de un trabajo en segundo plano de .NET. IronPDF maneja todo el ciclo de vida de la generación de PDF en C# en ironpdf.com, desde renderizar plantillas HTML hasta guardar, transmitir y manipular documentos. Si estás listo para reemplazar un proceso de informes manual por uno que se ejecute solo, comienza tu prueba gratuita de 30 días, es tiempo suficiente para crear una tubería programada, conectarla a tu base de datos y confirmar que los PDFs correctos lleguen a las bandejas de entrada correctas a tiempo.

Curtis Chau
Escritor Técnico

Curtis Chau tiene una licenciatura en Ciencias de la Computación (Carleton University) y se especializa en el desarrollo front-end con experiencia en Node.js, TypeScript, JavaScript y React. Apasionado por crear interfaces de usuario intuitivas y estéticamente agradables, disfruta trabajando con frameworks modernos y creando manuales bien ...

Leer más

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame