Como Criar Arquivos PDF em .NET Usando IronPDF (Tutorial C#)
O Problema Com Relatórios Recorrentes Manuais
Toda organização tem uma versão do mesmo ritual da manhã de segunda-feira: um analista abre o banco de dados de relatórios, executa as mesmas consultas que fez na semana passada, cola os números em um modelo de Excel, formata as tabelas, exporta para o formato PDF e envia por e-mail para a lista de distribuição de liderança, esperando que nada quebre e que o arquivo saia antes da reunião às 9h. Quando funciona, é um imposto invisível na manhã de alguém. Quando não funciona, como com dados desatualizados deixados em uma aba, um erro de fórmula na linha de resumo, um relatório que chega às 10h30 em vez de 7h, é visível para exatamente o público errado.
Ferramentas de BI existentes oferecem entrega agendada, mas geralmente como capturas de tela do painel ou links incorporados que exigem login. Um vice-presidente de vendas em trânsito não quer se autenticar em uma ferramenta de BI no celular, ele quer um relatório PDF que ele possa ler no tablet, encaminhar para um potencial cliente ou imprimir antes de uma reunião do conselho. Assinaturas agendadas do SSRS produzem documentos PDF autônomos, mas a saída parece software empresarial de 2010, e atualizar o modelo requer navegar por um designer de relatórios que a maioria das equipes .NET atuais não tem mais expertise.
Construir um pipeline personalizado do zero é a resposta arquitetônica correta, mas isso significa conectar um agendador, uma camada de consulta, um mecanismo de template e um remetente de e-mail, cada um um ponto de falha potencial que aciona alguém às 6h quando o resumo semanal de P&L do CFO não chega.
A Solução: Um Pipeline Agendado Com o IronPDF Como a Etapa de Renderização
IronPDF é uma biblioteca PDF .NET que atua como o motor de renderização dentro de um trabalho em segundo plano .NET agendado que executa consultas SQL contra o banco de dados de relatórios, mapeia os resultados para um modelo HTML e CSS, e envia o PDF finalizado para uma lista de distribuição em um cronograma, sem etapas manuais, sem necessidade de analista.
Hangfire, Quartz.NET ou um BackgroundService aciona o trabalho. ADO.NET ou Dapper puxa os números. O modelo HTML os transforma em um relatório com a marca. ChromePdfRenderer produces the PDF. O e-mail é enviado antes que alguém abra seu laptop. Sem capturas de tela de ferramentas de BI, sem definições de relatórios do SSRS, sem taxa de API por relatório. A renderização é executada dentro do aplicativo .NET existente, um pacote NuGet, sem processos externos.
Como funciona na prática
1. Cron Schedule Aciona o Trabalho
Cada relatório tem seu próprio cronograma: o instantâneo do pipeline de vendas é executado diariamente às 6h30, o resumo de P&L é executado na segunda-feira às 6h, o painel executivo mensal é executado no dia 1. O trabalho é registrado com sua expressão cron no Hangfire ou Quartz.NET na inicialização do aplicativo, ou implementado como um BackgroundService com tempo para implantações mais simples.
Quando o trabalho é acionado, ele abre uma conexão com o banco de dados de relatórios e executa as consultas SQL relevantes ou procedimentos armazenados para o período, números de receita e despesas, métricas de KPI, contagens de estágio do pipeline, taxas de conversão ou estatísticas de cumprimento, dependendo do tipo de relatório.
2. Resultados da Consulta Alimentam o Modelo HTML
Os resultados da consulta são mapeados em um modelo ou dicionário e inseridos no modelo HTML. O modelo define tudo sobre a aparência do relatório: o cabeçalho com a marca e o logotipo da empresa, uma etiqueta de período ("Semana de 27 de março de 2025"), cartões de métricas resumidas no topo, uma ou mais tabelas de dados com cores alternadas em fileiras, e um rodapé com o carimbo de geração de tempo e aviso de confidencialidade. Este exemplo de código IronPDF criará arquivos PDF que demonstram exatamente isso.
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)
Arquivo PDF Gerado

3. Documento PDF Enviado por E-mail e Arquivado
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)
E-mail de Exemplo com PDF Anexo
Listas de distribuição são extraídas de uma tabela de configuração em vez de codificadas, portanto, adicionar um novo destinatário ao relatório semanal de vendas não requer uma mudança de código ou uma reimplantação. Cada PDF arquivado é indexado pelo nome do relatório e data, construindo uma biblioteca de relatórios passados acessível a partir de uma unidade compartilhada ou portal interno.
O trabalho registra seu status de execução — duração da consulta, tempo de renderização, resultado de envio de e-mail e quaisquer erros — em logs estruturados. Um alerta é acionado se o trabalho não for concluído, para que o primeiro sinal de um problema não seja um executivo perguntando por que seu relatório não chegou.
Benefícios no mundo real
Esforço manual zero. Relatórios se geram e entregam por conta própria no cronograma. Nenhum analista executa consultas na manhã de segunda-feira, ninguém formata uma tabela, ninguém se lembra de clicar em enviar. O processo é invisível quando funciona e alertável quando não.
Pontualidade. O relatório chega às caixas de entrada antes do início do dia de trabalho. Um CFO verificando e-mails a caminho do escritório tem os números P&L antes de qualquer reunião precisar deles, não depois que um analista termina seu café.
Legibilidade offline. Um PDF autônomo que os executivos podem ler em um avião, imprimir para uma reunião do conselho ou encaminhar sem exigir que o destinatário faça login em qualquer sistema. Sem URL de painel, sem barreira de autenticação, sem "este link só funciona na rede corporativa".
Consistência de marca. Cada relatório produzido pelo pipeline usa o mesmo modelo HTML e CSS aprovado, o mesmo posicionamento de logotipo, tipografia, estilo de tabela e linguagem de rodapé em todos os relatórios semanais, diários e mensais que a organização distribui.
Arquivo histórico. Cada PDF gerado é armazenado por data em um caminho estruturado. Seis meses de relatórios de vendas semanais estão a uma pasta de distância para uma revisão de tendências ou uma solicitação de auditoria, sem necessidade de consulta de ferramenta de BI.
Sem custos por relatório. A renderização ocorre em processo dentro do aplicativo .NET existente. Não há necessidade de assento premium de ferramenta de BI para entrega programada e nenhum medidor de API de relatório que escala com o volume de relatórios ou tamanho da lista de distribuição.
Encerramento
Um relatório recorrente que chega de forma confiável antes do início do dia de trabalho é um pequeno detalhe operacional que afeta como as decisões são tomadas. A liderança que tem números antes da reunião matinal conduz um tipo de reunião diferente da liderança que espera até que um analista termine de formatar uma planilha.
O pipeline para chegar lá — agendador, consulta, modelo, renderização, e-mail — cabe confortavelmente dentro de um único trabalho em segundo plano .NET. IronPDF lida com o ciclo de vida completo da geração de PDF em C# em ironpdf.com, desde a renderização de modelos HTML até o salvamento, streaming e manipulação de documentos. Se você está pronto para substituir um processo de relatório manual por um que se executa sozinho, comece seu teste gratuito de 30 dias, é tempo suficiente para conectar um pipeline agendado, conectá-lo ao seu banco de dados e confirmar que os PDFs corretos chegam nas caixas de entrada certas a tempo.




