跳至頁尾內容
使用IRONPDF

如何在.NET中使用IronPDF建立PDF檔案(C#教程)

手動重複報告的問題

IronPDF首頁 每個組織都有相似的星期一早上儀式:分析師打開報告資料庫,執行上週相同的查詢,將資料粘貼到Excel模板中,格式化表格,匯出為PDF格式,並通過電子郵件發送到領導分發列表,希望不會有問題,並且文件能在上午9點會議前發出。當一切運行正常時,這是一種對某人早晨的隱形消耗。 當不順利時,比如選項卡中的陳舊資料、總結行的公式錯誤、報告在10:30而非7:00到達時,恰好會被錯誤的觀眾所看到。

現有的BI工具提供預定的交付,但通常是以儀表板截圖或嵌入的連結,需登入才能查看。 在通勤路上的銷售副總裁不想在手機上驗證BI工具,他們想要一份可以在平板電腦上閱讀的PDF報告,可以轉寄給潛在客戶,或在董事會會議前列印。 SSRS計劃訂閱產生自包含的PDF文件,但輸出看起來像2010年的企業軟體,更新模板需要瀏覽報告設計器,而當前的大多數.NET團隊已不再具備這方面的專業知識。

從頭開始構建一個自定義管道是正確的架構選擇,但這意味著要將調度器、查詢層、模板引擎和電子郵件發送器聯接起來,每個環節都有可能出錯,當CFO的每週P&L摘要未到達時,會在早上6點擾醒某人。

解決方案:使用IronPDF作為渲染步驟的預定管道

IronPDF是一個.NET PDF程式庫,作為預定.NET背景工作的渲染引擎。該工作執行SQL查詢以對抗報告資料庫,將結果映射到HTML和CSS模板上,並按cron計劃將完成的PDF電子郵件發送到分發列表,無需人工操作,無需分析師。

Hangfire、Quartz.NET或BackgroundService觸發該工作。 ADO.NET或Dapper提取資料。 HTML模板將這些資料轉化為有品牌的報告。 ChromePdfRenderer產生PDF。 電子郵件發送在任何人打開他們的筆記型電腦之前發出。沒有BI工具截圖,沒有SSRS報告定義,沒有每報告API費用。 渲染在現有.NET應用程式內運行,一個NuGet組件,沒有外部進程。

實務操作過程

1. Cron計劃觸發工作

每個報告都有自己的計劃:銷售管道快照每天上午6:30觸發,P&L摘要周一上午6:00觸發,月度執行儀表板在1號觸發。該工作在應用程式啟動時使用其cron表達式註冊到Hangfire或Quartz.NET,或實作為時間BackgroundService以便於部署。

當工作觸發時,它會開啟連接到報告資料庫,並執行對應的SQL查詢或儲存程式,根據報告型別及時段取得收入和支出數字、KPI指標、管道階段計數、轉換率或履行統計資料。

2. 查詢結果饋入HTML模板

查詢結果被映射到模型或字典中,並被插入到HTML模板中。 模板定義報告外觀的所有內容:附有公司標誌的品牌化標頭,期間標籤("2025年3月27日當週"),頂部的摘要指標卡,一個或多個具有交替行顏色的資料表,以及帶有生成時間戳和保密告示的頁腳。以下是IronPDF範例程式碼將建立展示這些內容的PDF文件。

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

生成的PDF文件

IronPDF範例生成的PDF

3. 電子郵件發送和檔案歸檔PDF文件

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

附加PDF的範例電子郵件

Scheduled Pdf Reports Email 3 related to 附加PDF的範例電子郵件 分發列表是從配置表中提取的,而不是硬編碼,因此新增新的接收者到每週銷售報告不需要程式碼更改或重新部署。 每個歸檔的PDF文件是按報告名稱和日期鍵入的,建立了一個可從共享驅動器或內部門戶存取的過去報告庫。

該工作記錄其執行狀態——查詢持續時間、渲染時間、電子郵件發送結果和任何錯誤——到結構日誌中。 如果工作未能完成,會發送警報,以便出現問題的第一個信號不是某位主管詢問為何他們的報告未能到達。

實際世界效益

零人工投入。報告按計劃自動生成和發送。 星期一早上沒有分析師跑查詢,沒有人格式化表格,沒有人記得按發送。 當流程運行正常時,這是不可見的;當出現問題時,會發送警報。

及時性。報告在工作日開始前抵達收件箱。 正在上班路上的CFO在任何會議需要之前已經獲得P&L數字,而不是在分析師喝完咖啡后才看到。

離線可讀性。一份自包含的PDF,行政人員可在飛機上閱讀、在董事會會議上列印,或轉寄而無需接收者登入任何系統。 沒有儀表板URL,沒有驗證牆,沒有"此連結只在公司網路上工作"。

品牌一致性。由管道生成的每份報告使用相同的核准HTML和CSS模板,相同的標誌位置、字體設計、表格樣式和頁腳語言,適用於組織發佈的每週、每日和每月報告。

歷史檔案。每份生成的PDF按日期儲存在結構化路徑中。 六個月的每週銷售報告僅需瀏覽一個資料夾便可進行趨勢審查或審核要求,無需BI工具查詢。

無每報告成本。渲染在現有的.NET應用程式中進行處理。 不需要購買BI工具高級席位以安排交付,沒有隨著報告量或分發列表大小增加的報告API計費。

結尾

在工作日開始前可靠到達的重複報告是對決策有影響的小運作細節。 擁有在早晨會議開始前數字的領導進行的會議,不同於等待分析師格式化電子表格的領導。

從調度、查詢、模板、渲染、電子郵件的管道完全可融入單一的.NET背景工作中。 IronPDF在ironpdf.com中處理PDF生成的整個生命週期,從渲染HTML模板到儲存、流式和操作文件。 如果您準備取代需要人工操作的報告流程,並以自動化的流程來代替,開始您的30天免費試用,這段時間足夠您接線預定管道、連接資料庫,並確認正確的PDF在正確的時間發送到正確的收件箱。

Curtis Chau
技術作家

Curtis Chau擁有Carleton大學的電腦科學學士學位,專精於前端開發,擁有Node.js、TypeScript、JavaScript和React的專業知識。Curtis熱衷於建立直觀且美觀的使用者介面,喜愛使用現代框架並建立結構良好、視覺吸引力的手冊。

除了開發,Curtis對物聯網(IoT)有濃厚的興趣,探索創新的方法來整合硬體和軟體。在空閒時間,他喜歡玩遊戲和建立Discord機器人,結合他對技術的熱愛與創造力。

Iron 支援團隊

我們線上24小時,每週5天。
聊天
電子郵件
給我打電話