フッターコンテンツにスキップ
IRONPDFの使用

.NETでIronPDFを使用してPDFファイルを作成する方法 (C#チュートリアル)

手動で定期的なレポートを作成する問題点

 IronPDFホームページ すべての組織には、同じ月曜日の朝の儀式のバージョンがあります。アナリストがレポートデータベースを開き、先週と同じクエリを実行し、数値をExcelのテンプレートに貼り付け、表をフォーマットし、PDF形式にエクスポートし、リーダーシップの配信リストにメールで送ります。何も問題が起こらず、ファイルが午前9時のスタンドアップの前に送信されることを祈りつつ、これがうまくいくと、誰かの朝にかかる見えない税金になります。 しかし、タブに古いデータが残っている場合や、サマリ行に数式エラーがある場合、午前10時30分に到着するレポートがある場合など、正反対の視聴者に対して見えるようになります。

既存のBIツールはスケジュール配信を提供しますが、通常はダッシュボードのスクリーンショットやログインを要求する埋め込みリンクとして提供されます。 通勤中の営業の副社長は、BIツールにスマートフォンで認証するのではなく、タブレットで読むことができ、見込み客に転送したり、取締役会の前にプリントしたりすることができるPDFレポートを望んでいます。 SSRSのスケジュールサブスクリプションは自己完結型のPDFドキュメントを生成しますが、出力は2010年のエンタープライズソフトウェアのようで、テンプレートを更新するには、現在 for .NETチームがもはや専門知識を持っていないレポートデザイナーを操作する必要があります。

ゼロからカスタムパイプラインを構築することは正しいアーキテクチャの解決策ですが、それはスケジューラ、クエリレイヤ、テンプレートエンジン、メール送信機能を結びつけることを意味します。それぞれがCFOの週次P&Lサマリが到着しない午前6時に誰かをページする可能性のある失敗点となります。

解決策:IronPDFをレンダリングステップとして使用したスケジュールパイプライン

IronPDFは、.NET PDFライブラリで、データベースに対してSQLクエリを実行し、結果をHTMLおよびCSSテンプレートにマッピングし、完了したPDFをcronスケジュールで配信リストにメールで送信するスケジュールされた.NETバックグラウンドジョブ内でレンダリングエンジンとして機能します。手動ステップは不要で、アナリストは必要ありません。

Hangfire、Quartz.NET、またはBackgroundServiceがジョブをトリガーします。 ADO.NETまたはDapperが数値を取得します。 HTMLテンプレートがそれらをブランド化されたレポートに変換します。 ChromePdfRendererがPDFを生成します。 メールは誰もラップトップを開く前に送信されます。BIツールのスクリーンショットなし、SSRSレポート定義なし、レポート別のAPI料金なし。 レンダリングは既存 for .NETアプリケーション内で実行され、1つのNuGetパッケージで、外部プロセスは不要です。

実際の使用方法

1. Cronスケジュールがジョブをトリガー

各レポートには独自のスケジュールがあります。販売パイプラインのスナップショットは午前6時30分に毎日実行され、P&Lサマリは月曜の午前6時に実行され、月次のエグゼクティブダッシュボードは1日に実行されます。ジョブは、HangfireまたはQuartz.NETでアプリケーションスタートアップ時にcron式で登録されるか、シンプルなデプロイメント用にtimed BackgroundServiceとして実装されます。

ジョブが実行されると、レポートデータベースに接続し、対象期間、収益および費用数値、KPIメトリック、パイプラインステージのカウント、変換率、またはレポートタイプに応じた履行統計に関連するSQLクエリやストアドプロシージャを実行します。

2. クエリ結果がHTMLテンプレートを供給

クエリ結果はモデルまたは辞書にマッピングされ、HTMLテンプレートに注入されます。 テンプレートはレポートの外観に関するすべてを定義します。会社のロゴを持つブランド化されたヘッダー、"2025年3月27日の週"という期間ラベル、上部に要約メトリックカード、交互の行色を持つ1つ以上のデータテーブル、生成タイムスタンプと機密性通知を持つフッター。この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は、構造化されたパスに日付別に保存されます。 販売の週次レポートの6か月分は、トレンドレビューや監査要求のためにフォルダをブラウズするだけで済み、BIツールのクエリは不要です。

レポートごとのコストなし。 レンダリングは、既存 for .NETアプリケーション内でインプロセスで実行されます。 スケジュールされた配信に要求されるBIツールプレミアムシートはなく、レポートのボリュームや配信リストのサイズに応じてスケールするレポートAPIメータリングはありません。

結び

作業日が始まる前に確実に届く定期レポートは、意思決定がどのように行われるかに影響を与える小さな運営上の詳細です。 朝のスタンドアップ前に数値が届くリーダーシップは、アナリストがスプレッドシートをフォーマットし終えるのを待つリーダーシップとは異なる種類の会議を運営します。

そこに到達するパイプライン—スケジューラ、クエリ、テンプレート、レンダリング、メール—は単一 for .NETバックグラウンドジョブに収まります。 ironpdf.comでのHTMLテンプレートのレンダリングから保存、ストリーミング、およびドキュメントの操作まで、IronPDFはC#でのPDF生成の完全なライフサイクルを処理します。 手動の報告プロセスを自動で実行するプロセスに置き換える準備ができたら、無料の30日間トライアルを開始してください。スケジュールされたパイプラインを接続し、データベースに接続し、正しいPDFが正しいメールボックスに時間内に届くことを確認するのに十分な時間です。

Curtis Chau
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

アイアンサポートチーム

私たちは週5日、24時間オンラインで対応しています。
チャット
メール
電話してね