IRONSOFTWAREHOME

Generate Reports in C# Like Crystal Reports (.NET 10)

Curtis Chau
Curtis Chau
Updated: 2026年6月4日

C# .NET中采用IronPDF进行HTML到PDF报告生成,取代Crystal Reports的专有.rpt设计器,使用标准的HTML、CSS和Razor模板,让.NET开发人员能够利用他们已有的Web开发技能来构建数据驱动的业务报告。 这包括完全支持动态表格、JavaScript 驱动的图表、条件格式化、多文档批处理以及在任何运行 .NET 的环境中进行跨平台部署。

TL;DR:快速入门指南

本教程涵盖用 C# .NET 生成 HTML-to-PDF 报告来取代 Crystal Reports,从基本模板到批处理和计划生成。

  • **适用对象:**替换 Crystal Reports 或从头开始构建新报表系统的 .NET 开发人员。
  • **您将构建的内容:**三个完整的报告实现(销售发票、员工目录、库存报告),Plus Chart.js 可视化、品牌页眉/页脚、目录生成、子报告合并和并行批处理。
  • 运行环境: .NET 10、.NET 8 LTS、.NET Framework 4.6.2+ 和 .NET Standard 2.0。无仅适用于 Windows 的 COM 依赖性。
  • **何时使用此方法:**当 Crystal Reports 缺乏 .NET Core 支持、Windows 锁定或复杂的许可成为瓶颈时。
  • 在技术上为何重要: HTML/CSS 在不同平台上的渲染效果完全相同,可与 CI/CD 集成,并可执行 JavaScript 图表,所有这一切都无需专有设计器或按文档收费。

要跟随示例代码,使用NuGet(IronPdf。 只需几行代码即可生成您的第一份报告:

  1. 1Install IronPDF with NuGet Package Manager

    PM > Install-Package IronPdf

  2. 2复制并运行这段代码。

    // Install-Package IronPdf
    var pdf = new IronPdf.ChromePdfRenderer()
        .RenderHtmlAsPdf("<h1>Sales Report</h1><table><tr><td>Q1</td><td>$50,000</td></tr></table>")
        .SaveAs("sales-report.pdf");
    C#
  3. 3部署到您的生产环境中进行测试

    通过免费试用立即在您的项目中开始使用IronPDF
    arrow pointer

购买或注册 IronPDF 30 天试用版后,请在应用程序的开头添加许可证密钥。

IronPdf.License.LicenseKey = "KEY";

Start using IronPDF in your project today with a free trial.

第一步:
arrow pointer
NuGet使用 NuGet 安装

PM > Install-Package IronPdf

Install IronPDF by running the command above in the NuGet Package Manager Console, or search for the package in the NuGet Package Manager.
目录

C# Report Generator: HTML Templates to PDF

HTML 到 PDF 的生成依赖于线性架构管道。应用程序使用标准数据模型填充 Razor 视图或 HTML 模板,而不是专有文件格式。 生成的HTML字符串然后被传递给渲染引擎,如IronPdf,其将视觉输出捕获为PDF文档。 这种方法将报告设计与托管环境分离开来,使完全相同的代码可以在任何支持 .NET 的平台上执行。

该工作流程反映了标准的网络开发。 前端开发人员使用 CSS 创建布局,并可在任何浏览器中立即预览。 然后,后端开发人员使用 C# 绑定数据。 这种分离允许团队使用现有的版本控制、代码审查和持续部署流程来处理报告,就像处理应用程序的其他部分一样。

HTML 可以实现 Crystal Reports 中无法实现的功能:交互式图表、响应式表格和共享样式,以实现一致的品牌形象。

为什么要在 .NET 应用程序中替换 Crystal Reports.

从 Crystal Reports 迁移到其他系统并不是单一灾难性问题或 SAP 突然放弃的结果。相反,它是各种摩擦点的累积,这些摩擦点共同导致新项目越来越难以证明该平台的合理性,现有解决方案的维护也更具挑战性。 找出这些痛点,就能明确为什么许多团队都在寻找替代方案,以及在评估替代方案时哪些标准是最重要的。

不支持 .NET 8 或 .NET Core.

Crystal Reports 不支持 .NET Core 或 .NET 5-10。SAP 在论坛上表示,他们不打算添加支持。 SDK 使用 COM 组件,与跨平台 .NET 不兼容。 对现代 .NET 的支持需要完全重写,而 SAP 拒绝这样做。

因此,在当前 .NET 版本上构建新应用程序的团队不能使用 Crystal Reports。 以 .NET 8 或 .NET 10 为标准的组织无法集成。 对于现有应用程序,升级到现代 .NET 运行时需要首先更换报告系统。

复杂的许可和隐藏成本

Crystal Reports 许可证区分为设计器许可证、运行时许可证、服务器部署和嵌入式使用。 桌面、网络和终端服务的规则各不相同。 在一种设置中符合要求,在另一种设置中可能需要额外的许可证。 如果在部署后出现漏洞,就会产生意想不到的成本。许多组织认为,这种不确定性比迁移到具有更明确许可的解决方案更糟糕。

仅限 Windows 平台锁定

Crystal Reports 只能在使用传统 .NET Framework 的 Windows 上运行。 您不能将这些应用程序部署到 Linux 容器、Azure App Service on Linux、AWS Lambda 或 Google Cloud Run。 随着企业使用容器化、平台无关和无服务器系统,这些限制变得更加重要。

构建微服务的开发团队面临着额外的挑战。 如果有九个服务在轻量级 Linux 容器中运行,而 Crystal Reports 需要使用 Windows,那么部署工作就会变得更加复杂。 您需要 Windows 容器镜像、Windows 兼容主机和单独的部署设置。 报告服务成为例外,阻碍了标准化。

Set Up a C# Report Generator in .NET 10

开始使用 IronPDF 非常简单。 像安装其他 .NET 依赖项一样,通过 NuGet 安装该库。 不需要下载额外的软件,也不需要为生产服务器单独安装运行时安装程序。

选择模板方法:Razor、HTML 或混合

IronPDF 支持三种不同的报告模板构建方法。 根据团队组成、项目要求和长期维护方面的考虑,每种方法都具有特定的优势。

Razor Views 为已经在 .NET 生态系统中工作的团队提供了最丰富的开发体验。 强类型模型在 Visual Studio 和 VS Code 中具有完整的 IntelliSense 支持、编译时检查以及 C# for 循环、条件、空处理和字符串格式化的全部功能。 Razor 的语法对于那些已经构建了 ASP.NET Core 应用程序的人来说非常熟悉,消除了与其他生态系统的模板引擎相关的学习曲线。 模板与其他源文件一起驻留在项目中,参与重构操作,并作为正常构建流程的一部分进行编译。

Plain HTML with String Interpolation 对于较简单的报告或喜欢将模板与 .NET 代码完全分开的团队非常适用。 HTML模板可以作为嵌入资源编译到程序集、部署在应用程序旁边的外部文件,甚至可以在运行时从数据库或内容管理系统检索。基本数据绑定使用单值的string.Replace(),或者更高级场景中像Scriban或Fluid这样的轻量级模板库。 这种方法最大限度地提高了可移植性,允许设计人员在不安装任何 .NET 工具的情况下编辑模板,只需使用文本编辑器和网络浏览器进行预览。

混合方法结合了这两种技术,适用于需要灵活性的场景。 例如,在渲染 Razor 视图时,可能会生成主要的 HTML 结构,然后对不适合视图模型的动态元素进行额外的字符串替换后处理。 另外,还可以加载非开发人员设计的 HTML 模板,并使用 Razor 部分视图在组合所有内容之前,只呈现复杂的数据驱动部分。 HTML 到 PDF 的转换与 HTML 源无关,您可以根据每份报告的需求混合使用不同的方法。

考虑到这些选项,本教程主要侧重于 Razor 视图,因为它们在类型安全性、可维护性和功能丰富度方面为典型的业务报表场景提供了最佳平衡。 如果未来的需求包括使用纯 HTML 模板,则技能可以直接转换,因为这两种方法都能生成 HTML 字符串。

Build a Data-Driven PDF Report in C#

本节演示了从头到尾创建销售发票报告的全过程。 该示例涵盖了所有报告使用的基本模式:定义一个数据结构模型,创建一个将数据转换为格式化 HTML 的 Razor 模板,将该模板渲染为 HTML 字符串,并将 HTML 转换为 PDF 文档,以便随时查看、通过电子邮件发送或存档。

创建 HTML/CSS 报告模板

第一步是定义数据模型。 真实的发票需要客户信息、带有说明和定价的细列项目、计算出的总额、税务处理以及公司品牌元素。 模型类的结构应反映这些分组:

// Invoice data model with customer, company, and line item details
public class InvoiceModel
{
    public string InvoiceNumber { get; set; } = string.Empty;
    public DateTime InvoiceDate { get; set; }
    public DateTime DueDate { get; set; }

    public CompanyInfo Company { get; set; } = new();
    public CustomerInfo Customer { get; set; } = new();
    public List<LineItem> Items { get; set; } = new();

    // Computed totals - business logic stays in the model
    public decimal Subtotal => Items.Sum(x => x.Total);
    public decimal TaxRate { get; set; } = 0.08m;
    public decimal TaxAmount => Subtotal * TaxRate;
    public decimal GrandTotal => Subtotal + TaxAmount;
}

// Company details for invoice header
public class CompanyInfo
{
    public string Name { get; set; } = string.Empty;
    public string Address { get; set; } = string.Empty;
    public string City { get; set; } = string.Empty;
    public string Phone { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string LogoPath { get; set; } = string.Empty;
}

// Customer billing information
public class CustomerInfo
{
    public string Name { get; set; } = string.Empty;
    public string Address { get; set; } = string.Empty;
    public string City { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

// Individual invoice line item
public class LineItem
{
    public string Description { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Total => Quantity * UnitPrice;
}

计算属性GrandTotal都包含在模型中。 这些计算属于模型而不是模板,使 Razor 视图专注于展示,而模型处理业务逻辑。 这种分离使单元测试变得简单明了,可以在不渲染任何 HTML 的情况下验证计算结果。

现在创建使得此模型转变为专业格式发票的Razor视图。将其保存为InvoiceTemplate.cshtml在您的Views文件夹中:

@model InvoiceModel
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
        /* Reset and base styles */
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Segoe UI', Arial, sans-serif; font-size: 12px; color: #333; line-height: 1.5; }
        .invoice-container { max-width: 800px; margin: 0 auto; padding: 40px; }

        /* Header with company info and invoice title */
        .header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #3498db; }
        .company-info h1 { font-size: 24px; color: #2c3e50; margin-bottom: 10px; }
        .company-info p { color: #7f8c8d; font-size: 11px; }
        .invoice-title { text-align: right; }
        .invoice-title h2 { font-size: 32px; color: #3498db; margin-bottom: 10px; }
        .invoice-title p { font-size: 12px; color: #7f8c8d; }

        /* Address blocks */
        .addresses { display: flex; justify-content: space-between; margin-bottom: 30px; }
        .address-block { width: 45%; }
        .address-block h3 { font-size: 11px; text-transform: uppercase; color: #95a5a6; margin-bottom: 8px; letter-spacing: 1px; }
        .address-block p { font-size: 12px; }

        /* Line items table */
        .items-table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }
        .items-table th { background-color: #3498db; color: white; padding: 12px; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
        .items-table th:last-child, .items-table td:last-child { text-align: right; }
        .items-table td { padding: 12px; border-bottom: 1px solid #ecf0f1; }
        .items-table tr:nth-child(even) { background-color: #f9f9f9; }

        /* Totals section */
        .totals { float: right; width: 300px; }
        .totals-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #ecf0f1; }
        .totals-row.grand-total { border-bottom: none; border-top: 2px solid #3498db; font-size: 16px; font-weight: bold; color: #2c3e50; padding-top: 12px; }

        /* Footer */
        .footer { clear: both; margin-top: 60px; padding-top: 20px; border-top: 1px solid #ecf0f1; text-align: center; color: #95a5a6; font-size: 10px; }
    </style>
</head>
<body>
    <div class="invoice-container">
        <!-- Company header and invoice details -->
        <div class="header">
            <div class="company-info">
                <h1>@Model.Company.Name</h1>
                <p>@Model.Company.Address</p>
                <p>@Model.Company.City</p>
                <p>@Model.Company.Phone | @Model.Company.Email</p>
            </div>
            <div class="invoice-title">
                <h2>INVOICE</h2>
                <p>Invoice #: @Model.InvoiceNumber</p>
                <p>Date: @Model.InvoiceDate.ToString("MMMM dd, yyyy")</p>
                <p>Due Date: @Model.DueDate.ToString("MMMM dd, yyyy")</p>
            </div>
        </div>

        <!-- Bill to address -->
        <div class="addresses">
            <div class="address-block">
                <h3>Bill To</h3>
                <p>@Model.Customer.Name</p>
                <p>@Model.Customer.Address</p>
                <p>@Model.Customer.City</p>
                <p>@Model.Customer.Email</p>
            </div>
        </div>

        <!-- Line items table -->
        <table class="items-table">
            <thead>
                <tr><th>Description</th><th>Quantity</th><th>Unit Price</th><th>Total</th></tr>
            </thead>
            <tbody>
                @foreach (var item in Model.Items)
                {
                    <tr>
                        <td>@item.Description</td>
                        <td>@item.Quantity</td>
                        <td>@item.UnitPrice.ToString("C")</td>
                        <td>@item.Total.ToString("C")</td>
                    </tr>
                }
            </tbody>
        </table>

        <!-- Totals -->
        <div class="totals">
            <div class="totals-row"><span>Subtotal:</span><span>@Model.Subtotal.ToString("C")</span></div>
            <div class="totals-row"><span>Tax (@(Model.TaxRate * 100)%):</span><span>@Model.TaxAmount.ToString("C")</span></div>
            <div class="totals-row grand-total"><span>Total Due:</span><span>@Model.GrandTotal.ToString("C")</span></div>
        </div>

        <!-- Footer message -->
        <div class="footer">
            <p>Thank you for your business!</p>
            <p>Payment is due within 30 days. Please include invoice number with your payment.</p>
        </div>
    </div>
</body>
</html>
HTML

该模板中嵌入的 CSS 可处理所有视觉样式,如颜色、字体、间距和表格格式。 IronPDF 还支持柔性框、网格布局和 CSS 变量等现代 CSS 功能。 渲染后的 PDF 与 Chrome 浏览器的打印预览完全匹配,这使得调试变得简单明了:如果 PDF 中的某些内容看起来不对,请在浏览器中打开 HTML,然后使用开发工具检查并调整样式。

将数据绑定到模板

模型和模板就位后,渲染PDF需要通过IronPDF的ChromePdfRenderer连接它们。 关键步骤是将 Razor 视图转换为 HTML 字符串,然后将该字符串传递给呈现器:

using IronPdf;

// Service class for generating invoice PDFs from Razor views
public class InvoiceReportService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IServiceProvider _serviceProvider;

    public InvoiceReportService(
        IRazorViewEngine razorViewEngine,
        ITempDataProvider tempDataProvider,
        IServiceProvider serviceProvider)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;
    }

    // Generate PDF from invoice model
    public async Task<byte[]> GenerateInvoicePdfAsync(InvoiceModel invoice)
    {
        // Render Razor view to HTML string
        string html = await RenderViewToStringAsync("InvoiceTemplate", invoice);

        // Configure PDF renderer with margins and paper size
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

        // Convert HTML to PDF and return bytes
        var pdfDocument = renderer.RenderHtmlAsPdf(html);
        return pdfDocument.BinaryData;
    }

    // Helper method to render a Razor view to string
    private async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
    {
        var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
        var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

        using var stringWriter = new StringWriter();
        var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);

        if (!viewResult.Success)
            throw new InvalidOperationException($"View '{viewName}' not found.");

        var viewDictionary = new ViewDataDictionary<TModel>(
            new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model };

        var viewContext = new ViewContext(actionContext, viewResult.View, viewDictionary,
            new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
            stringWriter, new HtmlHelperOptions());

        await viewResult.View.RenderAsync(viewContext);
        return stringWriter.ToString();
    }
}

对于更简单的情况(例如控制台应用程序或后台服务),当您不需要完整的ASP.NET Core MVC设置时,您可以只使用带有插值的HTML字符串和StringBuilder为动态部分。

输出示例

添加页眉、页脚和页码

专业报告通常包括所有页面一致的页眉和页脚,显示公司品牌、文档标题、生成日期和页码。 IronPDF 提供了两种实现这些元素的方法:基于文本的标题,用于需要最少格式化的简单内容;HTML 标题,用于带有徽标和自定义布局的完全样式控制。

基于文本的标题可以很好地提供基本信息,而且由于不需要额外的 HTML 解析,因此渲染速度更快:

using IronPdf;
using IronSoftware.Drawing;

// Configure text-based headers and footers
var renderer = new ChromePdfRenderer();

// Set starting page number
renderer.RenderingOptions.FirstPageNumber = 1;

// Add centered header with divider line
renderer.RenderingOptions.TextHeader = new TextHeaderFooter
{
    CenterText = "CONFIDENTIAL - Internal Use Only",
    DrawDividerLine = true,
    Font = FontTypes.Arial,
    FontSize = 10
};

// Add footer with date on left, page numbers on right
renderer.RenderingOptions.TextFooter = new TextHeaderFooter
{
    LeftText = "{date} {time}",
    RightText = "Page {page} of {total-pages}",
    DrawDividerLine = true,
    Font = FontTypes.Arial,
    FontSize = 9
};

// Set margins to accommodate header/footer
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 20;

可用的合并字段包括用于当前页码的{pdf-title}。 这些占位符会在渲染过程中自动替换。

对于带有徽标、自定义字体或复杂多栏布局的标题,请使用支持完整 CSS 样式的 HTML 标题:

using IronPdf;
using System;

var renderer = new ChromePdfRenderer();

// Configure HTML header with logo and custom layout
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    MaxHeight = 30,
    HtmlFragment = @"
<div style='display: flex; justify-content: space-between; align-items: center;
            width: 100%; font-family: Arial; font-size: 10px; color: #666;'>
    <img src='logo.png' style='height: 25px;'>
    <span>Company Name Inc.</span>
    <span>Invoice Report</span>
</div>",
    BaseUrl = new Uri(@"C:\assets\images\").AbsoluteUri
};

// Configure HTML footer with page info and generation date
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    MaxHeight = 20,
    HtmlFragment = @"
<div style='text-align: center; font-size: 9px; color: #999;
            border-top: 1px solid #ddd; padding-top: 5px;'>
    Page {page} of {total-pages} | Generated on {date}
</div>",
    DrawDividerLine = false
};

输出示例

创建动态表格和重复章节

报告通常需要显示跨越多个页面的数据集合。 Razor 的循环结构通过遍历集合并为每个项目生成表格行或卡片元素,自然而然地处理了这一问题。

下面是一个完整的 "员工目录 "示例,展示了以部门为单位的分组数据展示:

// Employee directory data models
public class EmployeeDirectoryModel
{
    public List<Department> Departments { get; set; } = new();
    public DateTime GeneratedDate { get; set; } = DateTime.Now;
}

// Department grouping with manager info
public class Department
{
    public string Name { get; set; } = string.Empty;
    public string ManagerName { get; set; } = string.Empty;
    public List<Employee> Employees { get; set; } = new();
}

// Individual employee details
public class Employee
{
    public string Name { get; set; } = string.Empty;
    public string Title { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string Phone { get; set; } = string.Empty;
    public string PhotoUrl { get; set; } = string.Empty;
    public DateTime HireDate { get; set; }
}

部门类上的CSS属性page-break-inside: avoid告诉PDF渲染器在可能的情况下将部门部分保持在同一页。 如果一个部门的内容会导致中途分页,渲染器会将整个部分移到下一页。 选择器page-break-before: always 强制每个部门从新页开始,创建整个目录的清晰部分分割。

输出示例

Advanced C# Report Generation With IronPDF

商业报告经常需要超越静态表格和文本的功能。 图表可以直观地反映趋势,而表格的形式则难以理解。 条件格式化可引起对需要采取行动的项目的注意。 子报告将多个来源的数据合并成具有凝聚力的文件。 本节内容包括使用 IronPDF 的 Chromium 渲染引擎实现上述各项功能。

在 PDF 报告中添加图表和图形

由于 JavaScript 在渲染过程中执行,因此您可以使用任何客户端图表库直接在报告中生成可视化效果。 图表将作为页面的一部分进行光栅化处理,并在最终的 PDF 中显示,与屏幕上一模一样。 Chart.js 在简洁性、功能和文档方面取得了极佳的平衡,可满足大多数报告需求。

从 CDN 包含 Chart.js,并使用从 C# 模型序列化的数据配置图表:

@model SalesReportModel

<!-- Include Chart.js library from CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<!-- Canvas element for chart rendering -->
<canvas id="salesChart"></canvas>

<script>
    // Initialize bar chart with data from C# model
    const ctx = document.getElementById('salesChart').getContext('2d');
    new Chart(ctx, {
        type: 'bar',
        data: {
            // Serialize model data to JavaScript arrays
            labels: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.MonthLabels)),
            datasets: [{
                label: 'Monthly Sales',
                data: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.MonthlySales)),
                backgroundColor: 'rgba(52, 152, 219, 0.7)'
            }]
        }
    });
</script>
HTML

在呈现包含 JavaScript 生成内容的页面时,应配置呈现器,等待脚本执行完毕后再捕获页面:

using IronPdf;

string html = "<h1>Report</h1>";

// Configure renderer to wait for JavaScript execution
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.WaitFor.JavaScript(500); // Wait 500ms for JS to complete
var pdf = renderer.RenderHtmlAsPdf(html);

输出示例

应用条件格式化和业务逻辑

可视化指示器可立即引起人们对需要采取行动的项目的注意,从而使库存报告受益匪浅。 条件格式化使例外情况一目了然,而不是强迫用户在数百行中寻找问题。 使用 Razor 的内联表达式根据数据值应用 CSS 类:

<!-- Iterate through inventory items sorted by quantity -->
@foreach (var item in Model.Items.OrderBy(x => x.Quantity))
{
    // Apply CSS class based on stock level thresholds
    var rowClass = item.Quantity <= Model.CriticalStockThreshold ? "stock-critical" :
                   item.Quantity <= Model.LowStockThreshold ? "stock-low" : "";

    <tr class="@rowClass">
        <td>@item.SKU</td>
        <td>@item.ProductName</td>
        <td class="text-right">
            <!-- Badge styling for critical stock items -->
            <span class="quantity-badge @(item.Quantity <= 5 ? "badge-critical" : "badge-ok")">
                @item.Quantity
            </span>
        </td>
    </tr>
}
HTML

输出示例

创建子报告和分节符

若要将独立生成的报告合并为一个文档,请使用 IronPDF 的合并功能:

using IronPdf;

// Combine multiple reports into a single PDF document
public byte[] GenerateCombinedReport(SalesReportModel sales, InventoryReportModel inventory)
{
    var renderer = new ChromePdfRenderer();

    // Render each report section separately
    var salesPdf = renderer.RenderHtmlAsPdf(RenderSalesReport(sales));
    var inventoryPdf = renderer.RenderHtmlAsPdf(RenderInventoryReport(inventory));

    // Merge PDFs into one document
    var combined = PdfDocument.Merge(salesPdf, inventoryPdf);
    return combined.BinaryData;
}

输出示例

生成目录

IronPDF 可根据 HTML 中的标题元素自动生成目录:

using IronPdf;

// Generate PDF with automatic table of contents
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.TableOfContents = TableOfContentsTypes.WithPageNumbers;
var pdf = renderer.RenderHtmlFileAsPdf("report.html");

我最喜欢的这种库是 IronPDF。它允许快速高效地操作 PDF 文件。它还具有许多有价值的功能,比如导出为 PDF/A 格式和数字签名 PDF 文档。

Milan Jovanovic

微软MVP

查看案例研究

IronOCR 意味着我们每年可以节省 $40,000 的人工处理成本,同时提高生产力,并释放资源用于高影响任务。我强烈推荐它。

Brent Matzelle

首席技术官,OPYN

查看案例研究

Iron Suite 在我们的运营中起着至关重要的作用。这些工具提高了业务各方面的效率,包括创建平面图和改善库存管理。

David Jones

首席软件工程师,Agorus Build

查看案例研究

从 Crystal Reports 迁移到 IronPDF.

迁移已建立的报告系统需要精心策划,以尽量减少中断,同时抓住机会实现现代化和简化。 通过了解 Crystal Reports 概念如何映射到基于 HTML 的方法,而不是试图从字面上复制每一个功能或保留原始报表的每一个怪癖,您的工作会进展得更快。

将 Crystal Reports 概念映射到 IronPDF.

了解概念映射有助于系统地翻译现有报告:

Crystal ReportsIronPDF 同等产品
报告章节带有 CSS 分页属性的 HTML divs
参数字段传递给 Razor 视图的模型属性
公式字段C# 模型类中的计算属性
运行总数LINQ 聚合
子报告部分视图或合并 PDF 文档
分组/排序将数据传递给模板之前的 LINQ 操作
交叉表报告使用嵌套循环的 HTML 表格
条件格式使用 CSS 类的 Razor @if 块

转换 .rpt 模板的最佳策略

请勿尝试以编程方式解析 .rpt 文件。 在翻译过程中,我们不能一概而论,而是要将现有的 PDF 输出视为可视化规范,并采用系统化的四步策略重建逻辑:

1.**目录:**列出所有 .rpt 文件的用途、数据来源和使用频率。 删除过时的报告,以缩小迁移范围。

2.**优先考虑:**首先迁移高频率报告。针对布局简单或存在持续维护问题的报告。

3.**参考文献:**将现有 Crystal Reports 导出为 PDF。 将这些内容作为视觉规范供开发人员匹配。

4.**验证:**使用生产数据量进行测试。 模板在渲染 10 行时速度很快,但在渲染 10,000 行时速度可能会变慢。

.NET 中的批量报告生成和调度

生产系统经常需要同时生成许多报告或按计划运行报告作业。 IronPDF 的线程安全设计可有效支持这两种情况。

并行生成多个报告

对于批处理,使用Task.WhenAll的异步模式:

using IronPdf;
using System.Collections.Concurrent;

// Generate multiple invoices in parallel using async processing
public async Task<List<ReportResult>> GenerateInvoiceBatchAsync(List<InvoiceModel> invoices)
{
    var results = new ConcurrentBag<ReportResult>();

    // Process invoices concurrently with controlled parallelism
    await Parallel.ForEachAsync(invoices,
        new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
        async (invoice, token) =>
        {
            // Each thread gets its own renderer instance
            var renderer = new ChromePdfRenderer();
            string html = BuildInvoiceHtml(invoice);
            var pdf = await renderer.RenderHtmlAsPdfAsync(html);

            // Save individual invoice PDF
            string filename = $"Invoice_{invoice.InvoiceNumber}.pdf";
            await pdf.SaveAsAsync(filename);

            results.Add(new ReportResult { InvoiceNumber = invoice.InvoiceNumber, Success = true });
        });

    return results.ToList();
}

输出示例

批处理示例可并行生成多张发票。 下面是生成的批量发票之一:

将报告生成与 ASP.NET Core 后台服务相结合

预定报告生成自然地融入了 ASP.NET Core 的托管服务基础架构:

// Background service for scheduled report generation
public class DailyReportService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // Calculate next run time (6 AM daily)
            var nextRun = DateTime.Now.Date.AddDays(1).AddHours(6);
            await Task.Delay(nextRun - DateTime.Now, stoppingToken);

            // Create scoped service for report generation
            using var scope = _serviceProvider.CreateScope();
            var reportService = scope.ServiceProvider.GetRequiredService<IReportGenerationService>();

            // Generate and distribute daily report
            var salesReport = await reportService.GenerateDailySalesSummaryAsync();
            // Email or save reports as needed
        }
    }
}

下载完整测试项目

本教程中的所有代码示例都包含在一个可随时运行的 .NET 10 测试项目中。 下载包括完整的源代码、数据模型、HTML 模板和可生成上述所有 PDF 示例的测试运行程序。

下一步

本指南中的示例展示了 IronPDF可处理各种业务报告需求:简单的发票(包含细列项目和总数)、包含分组数据和照片的复杂员工目录、包含条件格式和图表的库存报告、并行处理数百份文档的批处理,以及通过后台服务计划生成。

如果您正在评估现有 Crystal Reports 实施的替代方案,请从单个高价值报表开始。 使用此处显示的HTML-to-PDF模式重建它,比较开发经验和输出质量,然后再进行扩展。 许多团队发现,在建立模式和基础模板后,他们的第一份转换报告只需要几个小时,而在重复使用 Razor 模板和样式后,后续报告的编写只需要几分钟。 关于布局精度,像素完美呈现指南涵盖了如何使用 CSS 精确匹配 Crystal Reports 输出。

准备好开始构建了吗? 下载IronPDF并试用免费试用版。 同一个库可以处理从单个报告渲染到跨 .NET 环境的 大批量批量生成的所有工作。 如果您对迁移报告有疑问或需要架构指导,请联系我们的工程支持团队

常见问题解答

什么是IronPDF?

IronPDF 是一个 C# 库,使开发人员能够以编程方式创建、编辑和生成 PDF 文档,为 Crystal Reports 等传统报表工具提供了一个现代化的替代方案。

IronPDF 如何作为 Crystal Reports 的替代产品?

IronPDF 允许开发人员使用 HTML/CSS 模板,从而提供了一种灵活、现代的报表生成方法,这些模板可以轻松地进行样式设计和修改,而 Crystal Reports 的结构则较为僵化。

我可以使用 IronPDF 创建发票吗?

是的,您可以使用 IronPDF 的 HTML/CSS 模板创建详细的定制发票,轻松设计出专业外观的文档。

是否可以用 IronPDF 生成员工目录?

当然可以。IronPDF 允许您利用动态数据和 HTML/CSS 生成全面的员工目录,从而实现清晰有序的展示。

IronPDF 如何帮助编制库存报告?

IronPDF 可以通过使用 HTML/CSS 模板来简化库存报告的创建,这些模板可以动态填充数据,提供最新且具有视觉吸引力的报告。

在 IronPDF 中使用 HTML/CSS 模板有哪些优势?

在 IronPDF 中使用 HTML/CSS 模板可提供设计的灵活性、更新的便捷性以及与网络技术的兼容性,从而更容易维护和增强报告布局。

IronPDF 支持 .NET 10 吗?

是的,IronPDF 与 .NET 10 兼容,确保开发人员可以利用最新的 .NET 功能和改进来满足他们的报表生成需求。

IronPDF 如何提高报表生成速度?

IronPDF 对性能进行了优化,可以通过高效处理 HTML/CSS 并将其渲染为高质量的 PDF 文档来快速生成报告。

Is it possible to automate report generation with IronPDF?

IronPDF supports automation of report generation through batch processing and can be integrated with ASP.NET Core Background Services for scheduled report generation tasks.

What is the best approach to migrate from Crystal Reports to IronPDF?

The best approach involves mapping Crystal Reports concepts to their IronPDF equivalents, prioritizing high-use reports, referencing existing outputs as specifications, and validating the new implementations against production data.

Curtis Chau
技术作家

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

...
阅读更多

准备开始了吗?

Nuget Downloads 20,878,335版本:2026.9刚刚发布

立即获取您的免费30 天试用密钥
无需信用卡或创建账户
C# 用于 PDF 的 NuGet 库
通过 NuGet 安装

版本: 2026.9

PM > Install-Package IronPdf
nuget.org/packages/IronPdf/
  1. 在解决方案资源管理器中,右键点击引用,管理 NuGet 包
  2. 选择浏览并搜索 “IronPDF”
  3. 选择包并安装
C# PDF DLL
下载 DLL

版本: 2026.9

或在此处下载 Windows 安装程序。

  1. 下载并解压 IronPDF 到您的解决方案目录中的 ~/Libs 之类的位置
  2. 在 Visual Studio 解决方案资源管理器中,右键点击引用。选择浏览,“IronPDF.dll”

$999

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
预约您的免费现场演示
Booking Badge

深受全球数百万工程师信赖

Iron Software 的客户徽标
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户