跳至页脚内容
使用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日触发。作业在应用程序启动时在Hangfire或Quartz.NET中注册其cron表达式,或者实作为定时的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中处理C#中PDF生成的整个生命周期,从渲染HTML模板到保存、流式传输和操作文档。 如果您准备好用自动运行的流程替换手动报告流程,开始您的免费30天试用,这足够时间连接一个计划的管道,将其连接到您的数据库,并确认正确的PDF及时到达正确的收件箱。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我