Przejdź do treści stopki
KORZYSTANIE Z IRONPDF

Jak tworzyć pliki PDF w .NET przy użyciu IronPDF (samouczek w języku C#)

Problem z Ręcznymi Raportami Cyklu

Strona główna IronPDF Każda organizacja ma jakąś wersję tego samego poniedziałkowego rytuału: analityk otwiera bazę danych raportów, wykonuje te same zapytania co tydzień temu, wkleja liczby do szablonu Excel, formatuje tabele, eksportuje do formatu PDF i wysyła je na listę dystrybucyjną kierownictwa, mając nadzieję, że nic się nie zepsuje, a plik zostanie wysłany przed porannym spotkaniem o 9:00. Jeśli działa, jest to niewidoczny podatek na czyimś poranku. Kiedy nie działa, jak na przykład z przestarzałymi danymi pozostawionymi w karcie, błędem w formule w wierszu podsumowania, raportem, który dociera o 10:30 zamiast o 7:00, jest widoczny dla dokładnie niewłaściwego odbiorcy.

Istniejące narzędzia BI oferują dostarczanie według harmonogramu, ale zazwyczaj w postaci zrzutów ekranów pulpitów nawigacyjnych lub osadzonych linków, które wymagają logowania. Wiceprezes ds. sprzedaży podczas dojazdu do pracy nie chce się logować do narzędzia BI na swoim telefonie, chce raportu PDF, który można przeczytać na tablecie, przesłać potencjalnemu klientowi lub wydrukować przed spotkaniem zarządu. Subskrypcje harmonogramowe SSRS generują samodzielne dokumenty PDF, ale wynik wygląda jak oprogramowanie przedsiębiorstwa z 2010 roku, a aktualizacja szablonu wymaga nawigacji po projektancie raportów, z którym większość współczesnych zespołów .NET nie ma już doświadczenia.

Budowanie własnego potoku od podstaw to właściwe rozwiązanie architektoniczne, ale wymaga połączenia harmonogramu, warstwy zapytań, silnika szablonów i nadawcy e-maili, z których każdy jest potencjalnym punktem awarii, który budzi kogoś o 6:00, gdy tygodniowe podsumowanie P&L dyrektora finansowego nie dochodzi.

Rozwiązanie: Potok Harmonogramowy z IronPDF jako Krok Renderowania

IronPDF to biblioteka PDF dla .NET, która działa jako silnik renderowania w środowisku .NET, wykonującym zapytania SQL do bazy danych raportów, mapującym wyniki na szablon HTML i CSS, i wysyłającym ukończony PDF na listę dystrybucji w harmonogramie cron, bez ręcznych kroków, bez potrzeby analityka.

Hangfire, Quartz.NET lub BackgroundService uruchamiają zadanie. ADO.NET lub Dapper wyciągają liczby. Szablon HTML zamienia je w raport z marką. ChromePdfRenderer generuje PDF. E-mail wysyłany jest zanim ktokolwiek otworzy swojego laptopa. Brak zrzutów ekranów narzędzi BI, brak definicji raportów SSRS, brak opłat API na raport. Renderowanie uruchamia się wewnątrz istniejącej aplikacji .NET, jeden pakiet NuGet, brak zewnętrznych procesów.

Jak to działa w praktyce

1. Harmonogram Cron Uruchamia Zadanie

Każdy raport ma własny harmonogram: zdjęcie pipeline sprzedaży uruchamia się codziennie o 6:30, podsumowanie P&L uruchamia się w poniedziałek o 6:00, a miesięczny dashboard wykonawczy uruchamia się 1. dnia miesiąca. Zadanie jest rejestrowane z wyrażeniem cron w Hangfire lub Quartz.NET podczas uruchomienia aplikacji, lub zaimplementowane jako zaplanowany BackgroundService dla prostszych wdrożeń.

Gdy zadanie się uruchamia, otwiera połączenie z bazą danych raportów i wykonuje odpowiednie zapytania SQL lub procedury składowane na dany okres, dane dotyczące przychodów i wydatków, metryki KPI, liczbę etapów pipeline, wskaźniki konwersji lub wskaźniki realizacji, w zależności od typu raportu.

2. Wyniki Zapytania Zasilają Szablon HTML

Wyniki zapytania są mapowane do modelu lub słownika i wstrzykiwane do szablonu HTML. Szablon definiuje wszystko dotyczące wyglądu raportu: nagłówek z logo firmy, etykieta okresu ("Tydzień od 27 marca 2025"), karty metryk podsumowujących na górze, jedna lub więcej tabel danych z naprzemiennymi kolorami wierszy i stopką z czasem generowania oraz notką o poufności. Ten kod przykładowy IronPDF utworzy pliki PDF, które dokładnie to pokażą.

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

Wygenerowany Plik PDF

Przykład wygenerowanego PDF IronPDF

3. Dokument PDF Wysłany E-mailem i Zarchiwizowany

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

Przykładowy E-mail z Załączonym PDF

Scheduled Pdf Reports Email 3 related to Przykładowy E-mail z Załączonym PDF Listy dystrybucyjne są pobierane z tabeli konfiguracyjnej, a nie zakodowane na sztywno, więc dodanie nowego odbiorcy do tygodniowego raportu sprzedaży nie wymaga zmiany kodu ani ponownego wdrożenia. Każdy zarchiwizowany PDF jest identyfikowany według nazwy raportu i daty, tworząc bibliotekę wcześniejszych raportów dostępną z dysku współdzielonego lub wewnętrznego portalu.

Zadanie loguje swój status wykonania - czas zapytania, czas renderowania, wynik wysłania e-maila oraz wszelkie błędy - do strukturalnego logowania. Alert jest wysyłany, jeśli zadanie nie zakończy się powodzeniem, więc pierwszym sygnałem problemu nie jest pytanie kierownictwa, dlaczego nie dotarł ich raport.

Korzyści w praktyce

Zero wysiłku manualnego. Raporty generują się i dostarczają się same zgodnie z harmonogramem. Żaden analityk nie wykonuje zapytań w poniedziałkowy poranek, nikt nie formatuje tabeli, nikt nie pamięta o wysłaniu. Proces jest niewidoczny, gdy działa, i zgłaszany w przypadku awarii.

Terminowość. Raport trafia do skrzynek pocztowych przed rozpoczęciem dnia pracy. Dyrektor finansowy sprawdzający e-maile podczas drogi do biura ma dane P&L zanim jakiekolwiek spotkanie ich wymaga, nie po tym, jak analityk wypije kawę.

Możliwość czytania offline. Samodzielny PDF, który kierownictwo może przeczytać w samolocie, wydrukować na spotkanie zarządu lub przekazać dalej bez wymagania od odbiorcy zalogowania się do systemu. Brak adresu URL do pulpitu nawigacyjnego, brak ściany uwierzytelniania, brak "ten link działa tylko w sieci korporacyjnej".

Spójność marki. Każdy raport generowany przez potok używa tego samego zatwierdzonego szablonu HTML i CSS, to samo logo, typografii, stylizacji tabel i języka w stopce we wszystkich tygodniowych, dziennych i miesięcznych raportach rozprowadzanych przez organizację.

Archiwum historyczne. Każdy wygenerowany PDF jest przechowywany według daty w uporządkowanej ścieżce. Sześć miesięcy tygodniowych raportów sprzedaży to tylko przeglądanie folderów od przeglądu trendów lub audytu, brak wymaganego zapytania do narzędzia BI.

Brak kosztów na raport. Renderowanie działa wewnątrz istniejącej aplikacji .NET. Nie ma potrzeby premium seat w narzędziu BI dla dostaw według harmonogramu ani mierzenia API raportowania, które skaluje się wraz z objętością raportu lub wielkością listy dystrybucyjnej.

Zakończenie

Powtarzalny raport, który dociera niezawodnie przed rozpoczęciem dnia pracy, to mały szczegół operacyjny, który wpływa na podejmowanie decyzji. Kierownictwo, które ma liczby przed porannym spotkaniem, prowadzi innego rodzaju spotkanie niż kierownictwo, które czeka, aż analityk zakończy formatowanie arkusza kalkulacyjnego.

Potok, który do tego prowadzi - harmonogram, zapytanie, szablon, renderowanie, e-mail - mieści się wygodnie w jedynym .NET background job. IronPDF obsługuje pełen cykl życia generowania PDF w C# na ironpdf.com, od renderowania szablonów HTML po zapisywanie, streaming i manipulację dokumentami. Jeśli jesteś gotów zastąpić ręczny proces raportowania takim, który działa samodzielnie, rozpocznij bezpłatny 30-dniowy okres próbny, to wystarczający czas na połączenie harmonogramu, podłączenie go do bazy danych i potwierdzenie, że odpowiednie PDFy trafiają na właściwe skrzynki pocztowe na czas.

Curtis Chau
Autor tekstów technicznych

Curtis Chau posiada tytuł licencjata z informatyki (Uniwersytet Carleton) i specjalizuje się w front-endowym rozwoju, z ekspertką w Node.js, TypeScript, JavaScript i React. Pasjonuje się tworzeniem intuicyjnych i estetycznie przyjemnych interfejsów użytkownika, Curtis cieszy się pracą z nowoczesnymi frameworkami i tworzeniem dobrze zorganizowanych, atrakcyjnych wizualnie podrę...

Czytaj więcej

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie