Wie man PDF-Dateien in .NET mit IronPDF erstellt (C# Tutorial)
Das Problem mit manuellen wiederkehrenden Berichten
Jede Organisation hat eine Version desselben montagmorgendlichen Rituals: Ein Analyst öffnet die Berichts-Datenbank, führt dieselben Abfragen aus, die er letzte Woche ausgeführt hat, fügt die Zahlen in eine Excel-Vorlage ein, formatiert die Tabellen, exportiert sie in das PDF-Format und sendet sie per Email an die Führungsebene, in der Hoffnung, dass nichts schiefgeht und die Datei vor dem Stand-up um 9 Uhr versendet wird. Wenn es funktioniert, ist es eine unsichtbare Steuer auf jemandes Morgen. Wenn es nicht funktioniert, wie etwa bei veralteten Daten in einer Registerkarte, einem Formularfehler in der Zusammenfassungszeile oder einem Bericht, der um 10:30 statt um 7:00 eintrifft, ist es genau für das falsche Publikum sichtbar.
Bestehende BI-Tools bieten die geplante Zustellung an, meist jedoch als Dashboard-Screenshots oder eingebettete Links, die einen Login erfordern. Ein Verkaufsleiter auf dem Weg zur Arbeit möchte sich nicht auf einem BI-Tool auf seinem Telefon authentifizieren, er möchte einen PDF-Bericht, den er auf seinem Tablet lesen, an einen Interessenten weiterleiten oder vor einer Vorstandssitzung ausdrucken kann. SSRS-geplante Abonnements erzeugen in sich geschlossene PDF-Dokumente, aber das Ergebnis sieht aus wie Unternehmenssoftware von 2010, und die Aktualisierung der Vorlage erfordert die Navigation in einem Berichtsgenerator, mit dem die meisten aktuellen .NET-Teams keine Erfahrung mehr haben.
Der Aufbau einer benutzerdefinierten Pipeline von Grund auf ist die richtige architektonische Antwort, bedeutet jedoch das Zusammenfügen eines Planers, einer Abfrageebene, einer Vorlagen-Engine und eines E-Mail-Senders, wobei jeder ein potenzieller Fehlerpunkt ist, der jemanden um 6 Uhr morgens auf den Plan ruft, wenn die wöchentliche P&L-Zusammenfassung des CFO nicht eintrifft.
Die Lösung: Eine geplante Pipeline mit IronPDF als Rendering-Schritt
IronPDF ist eine .NET-PDF-Bibliothek, die als Rendering-Engine innerhalb eines geplanten .NET-Background-Jobs fungiert. Dieser führt SQL-Abfragen gegen die Berichts-Datenbank aus, überträgt die Ergebnisse auf eine HTML- und CSS-Vorlage und sendet das fertige PDF auf einem Cron-Zeitplan an eine Verteilerliste - keine manuellen Schritte, kein Analyst erforderlich.
Hangfire, Quartz.NET oder ein BackgroundService löst den Job aus. ADO.NET oder Dapper zieht die Zahlen. Die HTML-Vorlage verwandelt sie in einen Markenbericht. ChromePdfRenderer erzeugt das PDF. Die E-Mail wird verschickt, bevor jemand seinen Laptop öffnet. Keine BI-Tool-Screenshots, keine SSRS-Berichtsdefinitionen, keine per-Report-API-Gebühr. Das Rendering läuft innerhalb der bestehenden .NET-Anwendung, ein NuGet-Paket, keine externen Prozesse.
Wie es in der Praxis funktioniert
1. Cron-Zeitplan löst den Job aus
Jeder Bericht hat seinen eigenen Zeitplan: Der Sales-Pipeline-Snapshot wird täglich um 6:30 Uhr gestartet, die P&L-Zusammenfassung startet montags um 6:00 Uhr, das monatliche Executive Dashboard wird am 1. gestartet. Der Job wird mit seinem Cron-Ausdruck in Hangfire oder Quartz.NET beim Anwendungsstart registriert oder als zeitgesteuerter BackgroundService für einfachere Implementierungen eingerichtet.
Wenn der Job startet, öffnet er eine Verbindung zur Berichts-Datenbank und führt die relevanten SQL-Abfragen oder gespeicherten Prozeduren für den Zeitraum, die Umsatz- und Ausgabenwerte, die KPI-Kennzahlen, die Pipeline-Phasenanzahlen, die Umwandlungsraten oder die Abwicklungsstatistiken, abhängig vom Berichtstyp, aus.
2. Abfrageergebnisse füllen die HTML-Vorlage
Die Abfrageergebnisse werden in ein Modell oder ein Wörterbuch gemappt und in die HTML-Vorlage eingebettet. Die Vorlage definiert alles über das Aussehen des Berichts: der Marken-Header mit dem Firmenlogo, eine Periodenbezeichnung ("Woche vom 27. März 2025"), Zusammenfassungskarten oben, eine oder mehrere Datentabellen mit abwechselnden Zeilenfarben und ein Footer mit dem Generierungszeitstempel und dem Vertraulichkeitshinweis. Dieser IronPDF-Beispielcode wird PDF-Dateien erstellen, die genau dies demonstrieren.
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)
Erstellte PDF-Datei

3. PDF-Dokument per Email versendet und archiviert
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)
Beispiel-E-Mail mit angehängtem PDF
Verteilerlisten werden aus einer Konfigurationstabelle gezogen anstatt fest codiert, sodass das Hinzufügen eines neuen Empfängers für den wöchentlichen Verkaufsbericht keine Code-Änderung oder erneute Bereitstellung erfordert. Jedes archivierte PDF wird nach Berichtsname und Datum verschlüsselt, um eine Bibliothek vergangener Berichte zu erstellen, die von einem gemeinsam genutzten Laufwerk oder über ein internes Portal zugänglich ist.
Der Job protokolliert seinen Ausführungsstatus - Abfragedauer, Renderzeiten, Email-Sendeergebnis und etwaige Fehler - in einer strukturierten Protokollierung. Ein Alarm wird ausgelöst, wenn der Job nicht abgeschlossen wird, sodass das erste Anzeichen eines Problems nicht ein Manager ist, der fragt, warum sein Bericht nicht eingetroffen ist.
Nutzen in der realen Welt
Null manueller Aufwand. Berichte generieren und versenden sich selbst nach Zeitplan. Kein Analyst führt am Montagmorgen Abfragen durch, niemand formatiert eine Tabelle, niemand erinnert sich daran, auf 'Senden' zu drücken. Der Prozess ist unsichtbar, wenn er funktioniert, und meldungsfähig, wenn er es nicht tut.
Pünktlichkeit. Der Bericht landet in den Postfächern, bevor der Arbeitstag beginnt. Ein CFO, der auf dem Weg ins Büro E-Mails überprüft, hat die P&L-Zahlen, bevor irgendein Meeting sie erfordert, nicht erst nachdem ein Analyst seinen Kaffee beendet hat.
Offline-Lesbarkeit. Ein eigenständiges PDF, das Führungskräfte im Flugzeug lesen, für eine Vorstandssitzung ausdrucken oder weiterleiten können, ohne dass der Empfänger sich in ein System einloggen muss. Keine Dashboard-URL, keine Authentifizierungswand, kein "Dieser Link funktioniert nur im Firmennetzwerk."
Markenkonsistenz. Jeder von der Pipeline erstellte Bericht verwendet dieselbe genehmigte HTML- und CSS-Vorlage, dieselbe Logo-Platzierung, Typografie, Tabellenstil und Footer-Sprache in allen wöchentlichen, täglichen und monatlichen Berichten, die die Organisation verteilt.
Historisches Archiv. Jedes erstellte PDF wird nach Datum in einem strukturierten Pfad gespeichert. Sechs Monate wöchentlicher Verkaufsberichte sind nur einen Ordnerdurchlauf entfernt für eine Trendüberprüfung oder auf Anfrage bei einer Prüfung, keine BI-Tool-Abfrage erforderlich.
Keine Kosten pro Bericht. Das Rendering läuft prozessintern innerhalb der bestehenden .NET-Anwendung. Es ist kein BI-Tool-Premium-Sitzplatz für geplante Zustellung erforderlich, und es gibt keine Berichts-API-Messung, die mit dem Berichtsvolumen oder der Größe der Verteilerliste skaliert.
Abschluss
Ein wiederkehrender Bericht, der zuverlässig vor Beginn des Arbeitstags eintrifft, ist ein kleines operatives Detail, das beeinflusst, wie Entscheidungen getroffen werden. Eine Führungsebene, die vor dem morgendlichen Stand-up über Zahlen verfügt, führt eine andere Art von Meeting als eine Führungsebene, die wartet, bis ein Analyst das Formatieren einer Tabelle abgeschlossen hat.
Die Pipeline, um dort hinzukommen - Planer, Abfrage, Vorlage, Rendering, E-Mail - passt bequem in einen einzelnen .NET-Background-Job. IronPDF behandelt den gesamten Lebenszyklus der PDF-Erstellung in C# auf ironpdf.com, vom Rendering von HTML-Vorlagen bis zum Speichern, Streamen und Bearbeiten von Dokumenten. Wenn Sie bereit sind, einen manuellen Berichtsprozess durch einen zu ersetzen, der sich selbst ausführt, starten Sie Ihre kostenlose 30-Tage-Testversion, genug Zeit, um eine geplante Pipeline zu verkabeln, sie mit Ihrer Datenbank zu verbinden und zu bestätigen, dass die richtigen PDFs zur richtigen Zeit in den richtigen Postfächern landen.




