C#でレポートを生成する; Crystal Reports(.NET 10)のように。 カーティス・チャウ 更新日:2026年1月22日 IronPDF をダウンロード NuGet ダウンロード DLL ダウンロード Windows 版 無料トライアル LLM向けのコピー LLM向けのコピー LLM 用の Markdown としてページをコピーする ChatGPTで開く このページについてChatGPTに質問する ジェミニで開く このページについてGeminiに問い合わせる Grokで開く このページについてGrokに質問する 困惑の中で開く このページについてPerplexityに問い合わせる 共有する Facebook で共有 Xでシェア(Twitter) LinkedIn で共有 URLをコピー 記事をメールで送る This article was translated from English: Does it need improvement? Translated View the article in English HTMLからPDFへのレポート生成 in C# .NET with IronPDFは、Crystal Reports独自の.rptデザイナーを標準のHTML、CSS、Razorテンプレートに置き換え、.NET開発者がすでに持っているWeb開発スキルを使ってデータ駆動型のビジネスレポートを構築できるようにします。 これには、動的なテーブル、JavaScriptを使用したチャート、条件付き書式設定、複数ドキュメントのバッチ処理、および.NETを実行するあらゆる環境でのクロスプラットフォーム展開の完全なサポートが含まれます。 TL;DR:クイックスタートガイド。 このチュートリアルでは、基本的なテンプレートからバッチ処理、スケジュール生成まで、C# .NET での HTML-to-PDF レポート生成による Crystal Reports の置き換えについて説明します。 対象者: .NET開発者がCrystal Reportsを置き換えたり、ゼロから新しいレポートシステムを構築したりする場合。 構築するもの 3つの完全なレポート実装(売上請求書、従業員名簿、在庫レポート)、およびChart.jsによる視覚化、ブランド化されたヘッダー/フッター、目次の生成、サブレポートのマージ、並列バッチ処理。 Where it runs: .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をインストールしてください(Install-Package IronPdf)。 わずか数行のコードで最初のレポートを作成します: 今すぐ NuGet で PDF を作成してみましょう: NuGet パッケージ マネージャーを使用して IronPDF をインストールします PM > Install-Package IronPdf このコード スニペットをコピーして実行します。 // 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"); 実際の環境でテストするためにデプロイする 今すぐ無料トライアルでプロジェクトに IronPDF を使い始めましょう 30日間無料トライアル IronPDFを購入または30日間のトライアルにサインアップした後、アプリケーションの最初にライセンスキーを追加してください。 IronPdf.License.LicenseKey = "KEY"; IronPdf.License.LicenseKey = "KEY"; Imports IronPdf IronPdf.License.LicenseKey = "KEY" $vbLabelText $csharpLabel 今日あなたのプロジェクトでIronPDFを無料トライアルで使用開始。 最初のステップ: 無料で始める 購入の準備ができていませんか? PM > Install-Package IronPdf IronPDF を NuGet でチェックしてください。1000万回以上のダウンロードで、C#によるPDF開発を変革しています。 DLL または Windowsインストーラー をダウンロードすることもできます。 目次を参照してください。 TL;DR:クイックスタートガイド クイック概要。 HTMLテンプレートからPDFアーキテクチャへ C#レポートジェネレータ:HTML テンプレートから PDF .NETアプリケーションでCrystal Reportsを置き換える理由 .NET8または.NET Coreのサポートはありません。 複雑なライセンスと隠れたコスト。 Windows-Onlyプラットフォーム・ロックイン。 .NET10でC#レポートジェネレータをセットアップする テンプレートのアプローチを選択してください:Razor、HTML、またはハイブリッド C#でデータ駆動型PDFレポートを作成する HTML/CSSレポート・テンプレートを作成する。 データをテンプレートにバインドする。 ヘッダー、フッター、ページ番号を追加する。 動的なテーブルと繰り返しセクションを作成する。 IronPDFによる高度なC#レポート生成 PDFレポートにチャートとグラフを追加する。 条件付き書式設定とビジネスロジックを適用する。 サブレポートとセクション区切りを作成する。 目次を生成する。 CrystalレポートからIronPDFへ移行する Crystalレポートの概念をIronPDFにマップする。 .rptテンプレートを変換するための最善の戦略。 .NETでのバッチレポート生成とスケジューリング 複数のレポートを並行して生成する。 レポート生成をASP.NET Coreのバックグラウンドサービスと統合する。 完全なテストプロジェクトをダウンロードする。 テストプロジェクトのダウンロード。 C#レポートジェネレーター:HTML テンプレートから 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にデプロイすることはできません。 組織がコンテナ化、プラットフォーム非依存、サーバーレスシステムを使用するにつれて、これらの制限はより重要になります。 マイクロサービスを構築する開発チームは、さらなる課題に直面しています。 9つのサービスが軽量のLinuxコンテナで実行され、1つのサービスがCrystal ReportsのためにWindowsを必要とする場合、デプロイはより複雑になります。 Windowsコンテナイメージ、Windows互換ホスティング、個別のデプロイ設定が必要です。 レポーティング・サービスは例外となり、標準化を阻む。 .NET 10 で C#num; レポート ジェネレーターをセットアップする。 IronPDFを使い始めるのは簡単です。 他の.NET依存ライブラリと同様に、NuGetを通してライブラリをインストールしてください。 追加のソフトウェアをダウンロードしたり、本番サーバー用に別のランタイム・インストーラーをインストールしたりする必要はありません。 テンプレートアプローチの選択:Razor、HTML、ハイブリッド IronPdfはレポートテンプレートを作成するための3つの異なるアプローチをサポートしています。 各アプローチは、チームの構成、プロジェクトの要件、長期的なメンテナンスの考慮事項に応じて、特定の利点を提供します。 Razor Views は、すでに .NET エコシステムで作業しているチームに最も豊かな開発体験を提供します。 Visual StudioおよびVS Codeのインテリセンス、コンパイル時チェック、ループ、条件分岐、NULL処理、文字列フォーマットなど、C#の全機能をサポートする強力な型付けモデルが使用可能です。 Razorのシンタックスは、ASP.NET Coreアプリケーションを構築したことがある人にはなじみがあり、他のエコシステムのテンプレートエンジンに関連する学習曲線を排除します。 テンプレートは、他のソースファイルと一緒にプロジェクトに存在し、リファクタリング作業に参加し、通常のビルドプロセスの一部としてコンパイルされます。 文字列補間によるプレーンHTMLは、よりシンプルなレポートや、テンプレートを.NETコードから完全に分離することを好むチームには効果的です。 HTMLテンプレートは、アセンブリにコンパイルされた埋め込みリソース、アプリケーションと一緒にデプロイされた外部ファイル、あるいは実行時にデータベースやコンテンツ管理システムから取得したものとして保存することができます。基本的なデータバインディングでは、単一の値には string.Replace() を使用し、より高度なシナリオには Scriban や Fluid のような軽量テンプレートライブラリを使用します。 このアプローチは移植性を最大限に高め、デザイナーが.NETツールをインストールせずに、テキストエディタとプレビュー用のWebブラウザだけでテンプレートを編集できるようにします。 ハイブリッドアプローチは、柔軟性を必要とするシナリオのために、両方のテクニックを組み合わせたものです。 例えば、Razorのビューは、メインのHTML構造を生成するためにレンダリングされ、ビューモデルにきれいに収まらない動的要素のために文字列置換を追加して後処理されるかもしれません。 また、開発者以外がデザインしたHTMLテンプレートを読み込み、Razorのパーシャルビューを使用して、複雑なデータ駆動部分のみをレンダリングしてから、すべてを結合することもできます。 HTMLからPDFへの変換は、HTMLソースに依存しないため、各レポートのニーズに応じてアプローチを組み合わせることができます。 これらの選択肢の中で、このチュートリアルは主にRazorビューに焦点を当てます。なぜなら、Razorビューは典型的なビジネスレポーティングシナリオにおいて、型の安全性、保守性、機能の豊富さのバランスが最も優れているからです。 どちらの方法でもHTML文字列が生成されるため、将来的にプレーンなHTMLテンプレートでの作業が必要になった場合でも、スキルはそのまま引き継がれます。 C&numでデータ駆動型PDFレポートを作成する このセクションでは、最初から最後まで、売上請求書レポートの完全な作成を示します。 データを構造化するモデルを定義し、データをフォーマットされた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; } // 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; } Imports System Imports System.Collections.Generic Imports System.Linq ' Invoice data model with customer, company, and line item details Public Class InvoiceModel Public Property InvoiceNumber As String = String.Empty Public Property InvoiceDate As DateTime Public Property DueDate As DateTime Public Property Company As New CompanyInfo() Public Property Customer As New CustomerInfo() Public Property Items As New List(Of LineItem)() ' Computed totals - business logic stays in the model Public ReadOnly Property Subtotal As Decimal Get Return Items.Sum(Function(x) x.Total) End Get End Property Public Property TaxRate As Decimal = 0.08D Public ReadOnly Property TaxAmount As Decimal Get Return Subtotal * TaxRate End Get End Property Public ReadOnly Property GrandTotal As Decimal Get Return Subtotal + TaxAmount End Get End Property End Class ' Company details for invoice header Public Class CompanyInfo Public Property Name As String = String.Empty Public Property Address As String = String.Empty Public Property City As String = String.Empty Public Property Phone As String = String.Empty Public Property Email As String = String.Empty Public Property LogoPath As String = String.Empty End Class ' Customer billing information Public Class CustomerInfo Public Property Name As String = String.Empty Public Property Address As String = String.Empty Public Property City As String = String.Empty Public Property Email As String = String.Empty End Class ' Individual invoice line item Public Class LineItem Public Property Description As String = String.Empty Public Property Quantity As Integer Public Property UnitPrice As Decimal Public ReadOnly Property Total As Decimal Get Return Quantity * UnitPrice End Get End Property End Class $vbLabelText $csharpLabel Subtotal、TaxAmount、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"> <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> <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> <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> <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> <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> @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"> <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> <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> <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> <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> <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(); } } 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(); } } Imports IronPdf Imports Microsoft.AspNetCore.Mvc.Razor Imports Microsoft.AspNetCore.Mvc.ViewFeatures Imports Microsoft.Extensions.DependencyInjection Imports Microsoft.AspNetCore.Http Imports Microsoft.AspNetCore.Mvc Imports Microsoft.AspNetCore.Routing Imports System.IO Imports System.Threading.Tasks ' Service class for generating invoice PDFs from Razor views Public Class InvoiceReportService Private ReadOnly _razorViewEngine As IRazorViewEngine Private ReadOnly _tempDataProvider As ITempDataProvider Private ReadOnly _serviceProvider As IServiceProvider Public Sub New(razorViewEngine As IRazorViewEngine, tempDataProvider As ITempDataProvider, serviceProvider As IServiceProvider) _razorViewEngine = razorViewEngine _tempDataProvider = tempDataProvider _serviceProvider = serviceProvider End Sub ' Generate PDF from invoice model Public Async Function GenerateInvoicePdfAsync(invoice As InvoiceModel) As Task(Of Byte()) ' Render Razor view to HTML string Dim html As String = Await RenderViewToStringAsync("InvoiceTemplate", invoice) ' Configure PDF renderer with margins and paper size Dim renderer As 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 Dim pdfDocument = renderer.RenderHtmlAsPdf(html) Return pdfDocument.BinaryData End Function ' Helper method to render a Razor view to string Private Async Function RenderViewToStringAsync(Of TModel)(viewName As String, model As TModel) As Task(Of String) Dim httpContext As New DefaultHttpContext With {.RequestServices = _serviceProvider} Dim actionContext As New ActionContext(httpContext, New RouteData(), New ActionDescriptor()) Using stringWriter As New StringWriter() Dim viewResult = _razorViewEngine.FindView(actionContext, viewName, False) If Not viewResult.Success Then Throw New InvalidOperationException($"View '{viewName}' not found.") End If Dim viewDictionary As New ViewDataDictionary(Of TModel)( New EmptyModelMetadataProvider(), New ModelStateDictionary()) With {.Model = model} Dim viewContext As New ViewContext(actionContext, viewResult.View, viewDictionary, New TempDataDictionary(actionContext.HttpContext, _tempDataProvider), stringWriter, New HtmlHelperOptions()) Await viewResult.View.RenderAsync(viewContext) Return stringWriter.ToString() End Using End Function End Class $vbLabelText $csharpLabel コンソールアプリやバックグラウンドサービスのように、.NET Core MVCの完全なセットアップを必要としないシンプルなシナリオでは、動的な部分に補間とStringBuilderを使用したHTML文字列を使用できます。 サンプル出力 ヘッダー、フッター、ページ番号を追加する。 Professionalなレポートには通常、すべてのページで一貫したヘッダーとフッターが含まれ、会社のブランド名、文書のタイトル、生成日、ページ番号が表示されます。 IronPDFはこれらの要素を実装するために2つのアプローチを提供します: テキストベースのヘッダーは最小限のフォーマットでシンプルなコンテンツを、HTMLヘッダーはロゴやカスタムレイアウトで完全なスタイルコントロールを実現します。 テキストベースのヘッダーは、基本的な情報には適しており、HTMLの追加解析が不要なため、レンダリング速度も速くなります: :path=/static-assets/pdf/content-code-examples/tutorials/crystal-reports-alternative-csharp/text-headers-footers.cs 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; Imports IronPdf Imports IronSoftware.Drawing ' Configure text-based headers and footers Dim renderer As New ChromePdfRenderer() ' Set starting page number renderer.RenderingOptions.FirstPageNumber = 1 ' Add centered header with divider line renderer.RenderingOptions.TextHeader = New TextHeaderFooter With { .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 With { .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 $vbLabelText $csharpLabel 利用可能なマージフィールドには、現在のページ番号の{page}、ドキュメントの総ページ数の{total-pages}、生成タイムスタンプの{date}と{time}、ウェブページからレンダリングする場合のソースURLの{url}、ドキュメントのタイトルの{html-title}と{pdf-title}があります。 これらのプレースホルダーは、レンダリング中に自動的に置き換えられます。 ロゴ、カスタムフォント、複雑な複数カラムレイアウトのヘッダーには、完全なCSSスタイリングをサポートするHTMLヘッダーを使用してください: :path=/static-assets/pdf/content-code-examples/tutorials/crystal-reports-alternative-csharp/html-headers-footers.cs 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 }; Imports IronPdf Imports System Dim renderer As New ChromePdfRenderer() ' Configure HTML header with logo and custom layout renderer.RenderingOptions.HtmlHeader = New HtmlHeaderFooter With { .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 With { .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 } $vbLabelText $csharpLabel サンプル出力 動的なテーブルと繰り返しセクションを作成する レポートは、複数のページにまたがるデータの集合を表示する必要があることがよくあります。 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; } } // 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; } } ' Employee directory data models Public Class EmployeeDirectoryModel Public Property Departments As List(Of Department) = New List(Of Department)() Public Property GeneratedDate As DateTime = DateTime.Now End Class ' Department grouping with manager info Public Class Department Public Property Name As String = String.Empty Public Property ManagerName As String = String.Empty Public Property Employees As List(Of Employee) = New List(Of Employee)() End Class ' Individual employee details Public Class Employee Public Property Name As String = String.Empty Public Property Title As String = String.Empty Public Property Email As String = String.Empty Public Property Phone As String = String.Empty Public Property PhotoUrl As String = String.Empty Public Property HireDate As DateTime End Class $vbLabelText $csharpLabel departmentクラスのCSSプロパティpage-break-inside: avoidは、可能な限りdepartmentセクションを1ページにまとめるようPDFレンダラーに指示します。 部門のコンテンツがセクションの途中で改ページされる場合は、レンダラがセクション全体を次のページに移動します。 .department:not(:first-child)とpage-break-before: alwaysのセレクタは、最初の部署以降の各部署を強制的に新しいページから開始させ、ディレクトリ全体できれいなセクション区切りを作成します。 サンプル出力 IronPDFによる高度なC#レポート生成 ビジネスレポートでは、静的な表やテキスト以上の機能が求められることがよくあります。 グラフは、表形式では理解するのが面倒なトレンドを視覚化します。 条件付き書式は、アクションを必要とする項目に注意を向けます。 サブレポートは、複数のソースからのデータを統合した文書です。 このセクションでは、IronPDFのChromiumレンダリングエンジンを使ってそれぞれの機能を実装することについて説明します。 PDFレポートにチャートとグラフを追加する JavaScriptはレンダリング中に実行されるため、任意のクライアント側チャートライブラリを使用して、レポートに直接ビジュアライゼーションを生成することができます。 図表はページの一部としてラスタライズされ、最終的なPDFには画面とまったく同じように表示されます。 Chart.jsは、ほとんどのレポーティングニーズに対して、シンプルさ、機能、ドキュメントの優れたバランスを提供します。 CDNからChart.jsをインクルードし、C#モデルからシリアライズされたデータでチャートを構成します: @model SalesReportModel <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <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> @model SalesReportModel <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <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で生成されたコンテンツを含むページをレンダリングする場合は、スクリプトの実行が完了するまで待ってからページをキャプチャするようにレンダラーを設定してください: :path=/static-assets/pdf/content-code-examples/tutorials/crystal-reports-alternative-csharp/javascript-wait-rendering.cs 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); Imports IronPdf Dim html As String = "<h1>Report</h1>" ' Configure renderer to wait for JavaScript execution Dim renderer As New ChromePdfRenderer() renderer.RenderingOptions.WaitFor.JavaScript(500) ' Wait 500ms for JS to complete Dim pdf = renderer.RenderHtmlAsPdf(html) $vbLabelText $csharpLabel サンプル出力 条件付き書式とビジネスロジックを適用する。 インベントリレポートは、アクションが必要な項目にすぐに注意を向けることができる視覚的なインジケータが役立ちます。 ユーザーが何百行もの行をスキャンして問題を探すのではなく、条件付き書式を使用することで、例外を視覚的に明らかにすることができます。 Razor のインライン式を使用して、データ値に基づいて CSS クラスを適用します: @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"> <span class="quantity-badge @(item.Quantity <= 5 ? "badge-critical" : "badge-ok")"> @item.Quantity </span> </td> </tr> } @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"> <span class="quantity-badge @(item.Quantity <= 5 ? "badge-critical" : "badge-ok")"> @item.Quantity </span> </td> </tr> } HTML サンプル出力 サブレポートとセクション区切りを作成する 別々に作成されたレポートを1つのドキュメントにまとめるには、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; } 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; } Imports IronPdf ' Combine multiple reports into a single PDF document Public Function GenerateCombinedReport(sales As SalesReportModel, inventory As InventoryReportModel) As Byte() Dim renderer As New ChromePdfRenderer() ' Render each report section separately Dim salesPdf = renderer.RenderHtmlAsPdf(RenderSalesReport(sales)) Dim inventoryPdf = renderer.RenderHtmlAsPdf(RenderInventoryReport(inventory)) ' Merge PDFs into one document Dim combined = PdfDocument.Merge(salesPdf, inventoryPdf) Return combined.BinaryData End Function $vbLabelText $csharpLabel サンプル出力 目次の生成 IronPDFはHTMLの見出し要素に基づいて目次を自動的に生成することができます: :path=/static-assets/pdf/content-code-examples/tutorials/crystal-reports-alternative-csharp/table-of-contents.cs using IronPdf; // Generate PDF with automatic table of contents var renderer = new ChromePdfRenderer(); renderer.RenderingOptions.TableOfContents = TableOfContentsTypes.WithPageNumbers; var pdf = renderer.RenderHtmlFileAsPdf("report.html"); Imports IronPdf ' Generate PDF with automatic table of contents Dim renderer As New ChromePdfRenderer() renderer.RenderingOptions.TableOfContents = TableOfContentsTypes.WithPageNumbers Dim pdf = renderer.RenderHtmlFileAsPdf("report.html") $vbLabelText $csharpLabel クリスタルレポートからIronPDFへ移行する 確立されたレポートシステムを移行するには、混乱を最小限に抑えつつ、近代化と簡素化の機会を捉えるための慎重な計画が必要です。 Crystal Reports のコンセプトを HTML ベースのアプローチにどのように対応させるかを理解することで、元のレポートの機能を忠実に再現したり、癖を残したりする必要がなくなります。 クリスタルレポートの概念をIronPDFにマップする 概念マッピングを理解することで、既存のレポートを体系的に翻訳することができます: Crystal Reports IronPDF 同等物 レポートセクション HTML divs with CSS page-break プロパティ パラメータフィールド Razor ビューに渡されるモデルプロパティ 数式フィールド C# モデルクラスの計算プロパティ 合計 LINQ集約 サブレポート 部分的なビューまたはマージされたPDFドキュメント グループ化/ソート テンプレートにデータを渡す前のLINQ操作 クロスタブレポート ネストされたループを使用したHTMLテーブル 条件付き書式設定 CSSクラスによるRazor @ifブロック .rptテンプレートを変換するための最善の戦略 .rptファイルをプログラムで解析しようとしないでください。 その代わりに、既存のPDF出力を視覚的な仕様書として扱い、体系的な4つのステップ戦略を使用してロジックを再構築します: 1.インベントリ:すべての.rptファイルを、その目的、データソース、使用頻度とともにカタログ化します。 古いレポートを削除し、移行範囲を縮小する。 2.優先順位: 頻度の高いレポートを最初に移行します。シンプルなレイアウトのレポートや、メンテナンスに問題があるレポートを対象とします。 3.参考資料 既存のCrystal ReportsをPDFとしてエクスポートする。 開発者向けの視覚的な仕様書として使用してください。 4.検証:本番のデータ量でテストします。 10行で瞬時にレンダリングされるテンプレートは、10,000行では遅くなる可能性があります。 .NETにおけるレポートのバッチ生成とスケジューリング プロダクションシステムでは、多くのレポートを同時に生成したり、スケジュールに従ってレポートジョブを実行したりする必要があります。 IronPdfのスレッドセーフデザインは両方のシナリオを効率的にサポートします。 複数のレポートを並行して生成する バッチ処理には、Parallel.ForEachAsyncを使用するか、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(); } 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(); } Imports IronPdf Imports System.Collections.Concurrent Imports System.Threading.Tasks ' Generate multiple invoices in parallel using async processing Public Async Function GenerateInvoiceBatchAsync(invoices As List(Of InvoiceModel)) As Task(Of List(Of ReportResult)) Dim results As New ConcurrentBag(Of ReportResult)() ' Process invoices concurrently with controlled parallelism Await Task.Run(Async Function() Await Parallel.ForEachAsync(invoices, New ParallelOptions With {.MaxDegreeOfParallelism = Environment.ProcessorCount}, Async Function(invoice, token) ' Each thread gets its own renderer instance Dim renderer As New ChromePdfRenderer() Dim html As String = BuildInvoiceHtml(invoice) Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html) ' Save individual invoice PDF Dim filename As String = $"Invoice_{invoice.InvoiceNumber}.pdf" Await pdf.SaveAsAsync(filename) results.Add(New ReportResult With {.InvoiceNumber = invoice.InvoiceNumber, .Success = True}) End Function) End Function) Return results.ToList() End Function $vbLabelText $csharpLabel サンプル出力 バッチ処理の例では、複数の請求書を並行して作成します。 生成されたバッチ請求書の1つです: レポート生成をASP.NET Coreバックグラウンドサービスと統合する スケジュールされたレポート生成は、.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 } } } // 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 } } } Imports System Imports System.Threading Imports System.Threading.Tasks Imports Microsoft.Extensions.DependencyInjection ' Background service for scheduled report generation Public Class DailyReportService Inherits BackgroundService Private ReadOnly _serviceProvider As IServiceProvider Protected Overrides Async Function ExecuteAsync(stoppingToken As CancellationToken) As Task While Not stoppingToken.IsCancellationRequested ' Calculate next run time (6 AM daily) Dim nextRun = DateTime.Now.Date.AddDays(1).AddHours(6) Await Task.Delay(nextRun - DateTime.Now, stoppingToken) ' Create scoped service for report generation Using scope = _serviceProvider.CreateScope() Dim reportService = scope.ServiceProvider.GetRequiredService(Of IReportGenerationService)() ' Generate and distribute daily report Dim salesReport = Await reportService.GenerateDailySalesSummaryAsync() ' Email or save reports as needed End Using End While End Function End Class $vbLabelText $csharpLabel 完全なテスト プロジェクトをダウンロードする。 このチュートリアルのすべてのコード例は、すぐに実行できる.NET 10テストプロジェクトで利用できます。 ダウンロードには、完全なソースコード、データモデル、HTMLテンプレート、および上記のすべてのサンプルPDFを生成するテストランナーが含まれています。 次のステップ 本ガイドの例では、IronPDFがあらゆるビジネスレポーティングのニーズに対応できることを実証しています:シンプルな行項目と合計を含む請求書、グループ化されたデータと写真を含む複雑な従業員名簿、条件付き書式とグラフを含む在庫レポート、数百のドキュメントの並列バッチ処理、およびバックグラウンドサービスによるスケジュール生成などです。 既存の Crystal Reports の代替案を検討する場合は、価値の高いレポートを 1 つ作成することから始めます。 ここに示したHTML-to-PDFパターンを使って再構築し、開発経験と出力品質を比較し、そこから拡張してください。 多くのチームは、最初に変換したレポートは、パターンと基本テンプレートを確立するのに数時間かかり、その後のレポートは、Razorテンプレートとスタイリングを再利用するため、数分で組み立てることができます。 レイアウトの精度については、ピクセルパーフェクトレンダリングガイドで、CSS を使用して Crystal Reports の出力を正確に一致させる方法を説明しています。 構築開始の準備はできましたか? IronPdfをダウンロードして無料トライアルでお試しください。 同じライブラリで、単一のレポート レンダリングから、.NET 環境間での 大容量バッチ生成まで、すべてを処理します。 レポートの移行について質問がある場合、またはアーキテクチャのガイダンスが必要な場合は、当社のエンジニアリング・サポート・チームにお問い合わせください。 よくある質問 IronPDFとは? IronPdfは開発者がプログラムでPDFドキュメントを作成、編集、生成することを可能にするC#ライブラリで、Crystal Reportsのような伝統的なレポートツールに代わる最新のツールを提供します。 IronPdfはどのようにCrystal Reportsの代替となるのですか? IronPdfは、Crystal Reportsの堅苦しい構造とは対照的に、スタイリングや変更が容易なHTML/CSSテンプレートを開発者が使用できるようにすることで、レポート生成に柔軟でモダンなアプローチを提供します。 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ドキュメントにレンダリングすることで、レポートを素早く生成することができます。 カーティス・チャウ 今すぐエンジニアリングチームとチャット テクニカルライター Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。 準備はできましたか? Nuget ダウンロード 17,386,124 | バージョン: 2026.2 リリース NuGet 無料版 総ダウンロード数: 17,386,124 ライセンスを見る