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

This article was translated from English: Does it need improvement?
Translated
View the article in English

C# .NET ile IronPDF'de HTML'den PDF'ye rapor oluşturma, Crystal Reports'un özgün .rpt tasarımcısını standart HTML, CSS ve Razor şablonları ile değiştirerek .NET geliştiricilerinin zaten sahip oldukları web geliştirme becerilerini kullanarak veri odaklı iş raporları oluşturmalarını sağlar. Bu, dinamik tablolar, JavaScript ile desteklenen grafikler, koşullu formatlama, birden fazla doküman toplu işleme ve .NET çalıştıran herhangi bir ortamda çapraz platform uygulaması için tüm desteğini içerir.

Kısa Başlangıç Rehberi

Bu ders, C# .NET'te Crystal Reports'u HTML-to-PDF rapor oluşturma ile değiştirmeyi, temel şablonlardan toplu işlemeye ve programlı oluşturmaya kadar kapsar.

  • Kimin için: Crystal Reports'u değiştiren veya sıfırdan yeni raporlama sistemleri oluşturan .NET geliştiricileri için.
  • Ne yapacaksınız: Üç tam rapor uygulaması (satış faturası, çalışanlar rehberi, envanter raporu), yanı sıra Chart.js görüntülemeleri, marka başlıkları/altlıkları, içerik tablosu oluşturma, alt rapor birleştirme ve paralel toplu işleme.
  • Nerede çalışır: .NET 10, .NET 8 LTS, .NET Framework 4.6.2+ ve .NET Standard 2.0. Yalnızca Windows'a özgü COM bağımlılıkları yoktur.
  • Ne zaman bu yaklaşımı kullanmalı: Crystal Reports'un .NET Core desteği eksikliği, Windows bağımlılığı veya karmaşık lisanslama bir darboğaz haline geldiğinde.
  • Teknik olarak neden önemlidir: HTML/CSS, platformlar arasında aynı şekilde render edilir, CI/CD ile entegre olur ve grafikler için JavaScript çalıştırır, hiçbir özgün tasarımcı veya belge başına ücret olmadan.

Kod örneklerini takip etmek için, IronPDF'yi NuGet üzerinden (Install-Package IronPdf) yükleyin. Sadece birkaç satır kod ile ilk raporunuzu oluşturun:

  1. NuGet Paket Yöneticisi ile https://www.nuget.org/packages/IronPdf yükleyin

    PM > Install-Package IronPdf
  2. Bu kod parçasını kopyalayıp çalıştırın.

    // 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");
  3. Canlı ortamınızda test etmek için dağıtın

    Bugün projenizde IronPDF kullanmaya başlayın ücretsiz deneme ile

    arrow pointer

IronPDF'nin 30 günlük deneme sürümünü satın aldıktan veya kaydolduktan sonra, uygulamanızın başlangıcında lisans anahtarınızı ekleyin.

IronPdf.License.LicenseKey = "KEY";
IronPdf.License.LicenseKey = "KEY";
Imports IronPdf

IronPdf.License.LicenseKey = "KEY"
$vbLabelText   $csharpLabel

!{--0100110001001001010000100101001001000001010100100101100101011111--}

NuGet NuGet ile Yükle

PM >  Install-Package IronPdf

IronPDF üzerine NuGet için hızlı bir kurulum kontrol edin. 10 milyondan fazla indirme ile C# ile PDF geliştirmesini dönüştürüyor. DLL veya Windows yükleyicisini de indirebilirsiniz.

İçindekiler

C# Report Generator: HTML Templates to PDF

HTML'den-PDF'ye dönüşüm, lineer mimari bir hattı kullanır. Özgün bir dosya formatı yerine, uygulama, Razor görünümlerini veya HTML şablonlarını doldurmak için standart veri modelleri kullanır. Sonuç olarak elde edilen HTML dizesi, görüntü çıktısını PDF dokümanı olarak yakalayan IronPDF gibi bir işleme motoruna iletilir. Bu yaklaşım, rapor tasarımını hosting ortamından ayırır ve aynı kodun .NET'i destekleyen her platformda çalışmasına olanak sunar.

Bu iş akışı, standart web geliştirmeyi andırır. Front-end geliştiriciler, CSS kullanarak layout'u oluşturur ve bunları hemen herhangi bir tarayıcıda önizler. Ardından, backend geliştiricileri veriyi C# kullanarak bağlar. Bu ayrım, takımların raporlar için var olan sürüm kontrol, kod incelemesi ve sürekli dağıtım süreçlerini, aynı uygulamanın diğer kısımları için kullandıkları gibi kullanmalarına olanak tanır.

HTML, Crystal Reports'un sağlayamadığı özellikleri sunar: etkileşimli grafikler, duyarlı tablolar ve tutarlı markalama için paylaşılan stiller.

Crystal Reports'u .NET Uygulamalarında Neden Değiştirmeli

Crystal Reports'tan uzaklaşmanın nedeni tek bir büyük sorun veya SAP'ın ani bir bırakması değildir. Bunun yerine, platformun yeni projeler için giderek gereksiz hale gelmesine ve mevcut çözümlerde daha zorlu hale gelmesine neden olan sürtünme noktalarının birikimidir. Bu acı noktaları belirlemek, birçok takımın neden alternatif arayışında olduğunu ve değiştirme seçeneklerini değerlendirirken en önemli kriterlerin neler olduğunu netleştirir.

.NET 8 veya .NET Core Desteklenmiyor

Crystal Reports, .NET Core veya .NET 5-10'u desteklemez. SAP, forumlarda destek eklemeyi planlamadıklarını belirtmiştir. SDK, platformlar arası .NET ile uyumsuz olan COM bileşenlerini kullanır. Modern .NET desteği, komple bir yeniden yazma gerektirecektir, ki SAP bunu yapmayı reddetmiştir.

Sonuç olarak, güncel .NET sürümleri üzerine yeni uygulama geliştiren takımlar Crystal Reports'u kullanamaz. NET 8 veya .NET 10 standardizasyonu yapan organizasyonlar, entegrasyon sağlayamaz. Mevcut uygulamalarda, modern .NET çalıştırma ortamına yükseltme, raporlama sisteminin önce değiştirilmesini gerektirir.

Karmaşık Lisanslama ve Gizli Maliyetler

Crystal Reports lisanslaması, tasarımcı lisansları, çalışma zamanı lisansları, sunucu dağıtımları ve gömülü kullanım arasında ayrım yapar. Kurallar masaüstü, web ve terminal hizmetleri için değişir. Bir kurulumda uyum sağlamak diğerinde ek lisanslar gerektirebilir. Dağıtımdan sonra boşluklar ortaya çıkarsa, beklenmedik maliyetler ortaya çıkar. Birçok organizasyon, belirsizliğin daha net bir lisanslamaya sahip bir çözümle taşınmaktan daha kötü olduğuna karar verir.

Sadece Windows Tabanlı Platform Kısıtlamaları

Crystal Reports yalnızca Windows'ta, eski .NET Framework ile çalışır. Bu uygulamaları Linux konteynerlerine, Azure Linux'ta App Service'e, AWS Lambda'ya veya Google Cloud Run'a dağıtamazsınız. Organizasyonlar konteynerli, platformdan bağımsız ve sunucusuz sistemler kullandıkça, bu kısıtlamalar daha önemli hale gelir.

Mikro servisler geliştiren takımlar, ek zorluklarla karşılaşır. Eğer dokuz hizmet hafif Linux konteynerlerinde çalışırken biri Crystal Reports için Windows gerektirirse, dağıtım daha karmaşık hale gelir. Windows konteyner görüntüleri, Windows uyumlu barındırma ve ayrı dağıtım ayarlarına ihtiyaçınız vardır. Raporlama hizmeti standartlaşmayı engelleyerek bir istisna haline gelir.

Set Up a C# Report Generator in .NET 10

IronPDF ile başlamak basittir. Kütüphaneyi diğer .NET bağımlılıkları gibi NuGet ile yükleyin. Ek indirme veya üretim sunucuları için ayrı çalıştırma zamanı yükleyici yoktur.

Bir Şablon Yöntemi Seçin: Razor, HTML veya Hibrit

IronPDF, rapor şablonları oluşturmak için üç farklı yaklaşımı destekler. Her yaklaşım, takımın bileşimine, proje gereksinimlerine ve uzun vadeli bakım hususlarına bağlı olarak belirli avantajlar sağlar.

Razor Görünümleri, zaten .NET ekosisteminde çalışan takımlar için en zengin geliştirme deneyimini sunar. Visual Studio ve VS Code'da tam IntelliSense desteği ile güçlü tür güvenli modeller, derleme zamanında kontrol, döngü, koşullar, null işleme ve dize formatlama için C# gücünün tamamı mevcuttur. Razor'un söz dizimi, ASP.NET Core uygulamaları geliştirmiş olanlara tanıdık gelir, diğer ekosistemlerden gelen şablon motorlarıyla ilgili öğrenme eğrisini ortadan kaldırır. Şablonlar, diğer kaynak dosyalarıyla birlikte projede yer alır, yeniden adlandırma işlemlerine katılır ve normal derleme sürecinin bir parçası olarak derlenir.

Düz HTML ile Dize Enterpolasyonu, daha basit raporlar için veya şablonları .NET kodundan tamamen ayırmayı tercih eden takımlar için iyi çalışır. HTML şablonları, derlemeye eklenmiş gömülü kaynaklar olarak saklanabilir, uygulamayla birlikte çalıştırılabilir dış dosyalar ya da çalışma zamanında bir veritabanından veya içerik yönetim sisteminden alınabilir. Temel veri bağlantısı tekli değerler için string.Replace() kullanır veya daha gelişmiş senaryolar için Scriban veya Fluid gibi hafif bir şablon kütüphanesi kullanır. Bu yaklaşım, taşınabilirliği en üst düzeye çıkararak tasarımcıların sadece bir metin düzenleyici ve önizleme için bir web tarayıcı kullanarak, .NET araçları kurulumuna gerek kalmadan şablonları düzenlemesine olanak tanır.

Hibrit Yaklaşımlar esneklik gerektiren durumlar için her iki tekniği birleştirir. Örneğin, bir Razor görünümü ana HTML yapısını oluşturmak için oluşturulmuş olabilir, ardından görüntü modeline tam olarak uymayan dinamik öğeler için ek dize değiştirmelerle işlenebilir. Alternatif olarak, bir geliştirici olmayan bir kişi tarafından tasarlanan HTML şablonu yüklenebilir ve yalnızca karmaşık, veri odaklı bölümleri işlemek için Razor kısmi görünümleri kullanılabilir ve sonra her şey birleştirilir. HTML'den PDF'ye dönüştürme, HTML kaynağına bağımlı olmaz, böylece her raporun ihtiyaçlarına göre yaklaşımları karıştırma olanağı tanır.

Bu seçenekler göz önüne alındığında, bu öğretici, tip güvenliği, bakımı ve özellik zenginliğinin en iyi dengesini sundukları için esasen Razor görünümlerine odaklanıyor. Gelecekteki gereksinimler, düz HTML şablonlarıyla çalışma dahil olduğunda, becerilerin doğrudan aktarımı vardır çünkü her iki yöntem de HTML dizeleri üretir.

Build a Data-Driven PDF Report in C

Bu bölüm, baştan sona bir satış faturası raporunun tamamen oluşturulmasını gösterir. Örnek, tüm raporlar için kullanılan temel deseni kapsar: verileri yapılandıran bir model tanımlayın, verileri biçimlendirilmiş HTML'ye dönüştüren bir Razor şablonu oluşturun, bu şablonu bir HTML dizesine işleyin ve HTML'yi görme, e-posta iletme veya arşivleme için hazır olan bir PDF belgesine dönüştürün.

HTML/CSS Rapor Şablonu Oluşturun

İlk adım, veri modelini tanımlamaktır. Gerçek bir fatura, müşteri bilgilerini, açıklamalar ve fiyatlarla satır öğelerini, hesaplanan toplamları, vergi işlemesini ve şirket markalama unsurlarını gerektirir. Model sınıfları, bu gruplamaları yansıtacak şekilde yapılandırılmalıdır:

// 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

Aratoplam, Vergi Tutarı ve Genel Toplam gibi hesaplanmış özellikler modele dahil edilmiştir. Bu hesaplamalar şablondan ziyade modele aittir, Razor görünümlerini sunuma odaklı tutarken model iş mantığını ele alır. Bu ayrım, birim testlerini kolaylaştırır, HTML işlenmeden hesaplamaların doğrulanmasına olanak tanır.

Bu modeli profesyonel olarak formatlanmış bir faturaya dönüştüren Razor görünümünü şimdi oluşturun. Bunu Views klasörünüze InvoiceTemplate.cshtml şeklinde kaydedin:

@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

Bu şablona gömülü CSS, renkler, yazı tipleri, boşluklar ve tablo biçimlendirme gibi tüm görsel stilleri ele alır. IronPDF ayrıca flexbox, grid düzenleri ve CSS değişkenleri gibi modern CSS özelliklerini de destekler. İşlenmiş PDF, Chrome'un baskı önizlemesini aynen yansıtır, bu da hata ayıklamayı kolaylaştırır: PDF'de bir şey yanlış görünüyorsa, HTML'yi bir tarayıcıda açın ve geliştirici araçlarını kullanarak stilleri inceleyin ve ayarlayın.

Verileri Şablona Bağlayın

Model ve şablon hazır olduğunda, PDF'yi işlemek, bunları IronPDF'in ChromePdfRenderer'ı aracılığıyla bağlamayı gerektirir. Temel adım, Razor görünümünü bir HTML dizesine dönüştürmek ve sonra bu dizeyi işleyiciye geçirmek:

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

Tam ASP.NET Core MVC kurulumuna ihtiyaçınız olmadığında, örneğin bir konsol uygulaması veya arka plan hizmetinde olduğu gibi, dinamik kısımlar için dize enterpolasyonu ve StringBuilder ile sadece HTML dizelerini kullanabilirsiniz.

Örnek Çıktı

Başlıklar, Alt Bilgiler ve Sayfa Numaraları Ekleyin

Profesyonel raporlar, genellikle şirket markalamaları, belge başlıkları, oluşturma tarihleri, ve sayfa numaraları gösteren tüm sayfalarda tutarlı başlıklar ve alt bilgiler içerir. IronPDF, bu unsurları uygulamak için iki yaklaşım sunar: minimum formatlama gerektiren basit içerik için metin tabanlı başlıklar ve tam stil kontrolü ile logolar ve özel düzenler için HTML başlıklar.

Text-based headers work well for basic information and render faster since they don't require additional HTML parsing:

: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

Kullanılabilir birleştirme alanları arasında mevcut sayfa numarası için {page}, belgenin toplam sayfa sayısı için {total-pages}, oluşturma zaman damgaları için {date} ve {time}, bir web sayfasından render ediliyorsa kaynak URL için {url} ve belge başlıkları için {html-title} ve {pdf-title} bulunur. Bu yer tutucular, işleme sırasında otomatik olarak değiştirilir.

Logolar, özel yazı tipleri veya karmaşık çok kolonlu düzenlerle başlıklar için, tam CSS stil desteğine sahip HTML başlıklar kullanın:

: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

Örnek Çıktı

Dinamik Tablolar ve Tekrarlanan Bölümler Oluşturma

Raporlar, genellikle birden fazla sayfaya yayılan veri koleksiyonlarını göstermek zorundadır. Razor'un döngü konstruksiyonları, koleksiyonlar üzerinde yineleme yaparak ve her bir öğe için tablo satırları veya kart elemanları üreterek bunu doğal bir şekilde ele alır.

İşte departman bölümleriyle grup verisi sunumu gösteren eksiksiz bir Çalışan Dizini örneği:

// 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 sınıfındaki page-break-inside: avoid CSS özelliği, mümkünse department bölümlerinin tek bir sayfada bir arada tutulmasını PDF render motoruna söyler. Bir departmanın içeriği, bir bölüm ortasında bir sayfa kesilmesine neden olursa, işleyici tüm bölümü bir sonraki sayfaya taşır. Directory boyunca temiz bölüm ayrımı oluşturarak, ilk department'tan sonraki her department'ı yeni bir sayfada başlatmaya zorlayan .department:not(:first-child) ve page-break-before: always seçicisi.

Örnek Çıktı

Advanced C# Report Generation With IronPDF

İş raporları sıkça statik tablolar ve metinlerin ötesinde yetenekler gerektirir. Trendleri tablo biçiminde anlamak zahmetli olacağından, grafikler görsel hale getirir. Koşullu formatlama, aksiyon gerektiren öğelere dikkat çeker. Alt raporlar, birden fazla kaynaktan gelen verileri uyumlu belgelerde birleştirir. Bu bölüm, IronPDF'in Chromium renderleme motorunu kullanarak bu özelliklerden her birini uygulamayı kapsar.

PDF Raporlarına Grafikler ve Çizelgeler Ekleyin

JavaScript işlemler sırasında çalıştığı için, doğrudan raporlarınıza görselleştirme oluşturmak için herhangi bir istemci-taraflı grafik kütüphanesini kullanabilirsiniz. Grafik, sayfanın bir parçası olarak rasterize edilir ve ekranda nasıl görünüyorsa nihai PDF'de de aynen görünür. Chart.js, çoğu raporlama gereksinimi için basitlik, yetenek ve dokümantasyonun mükemmel dengesini sunar.

Chart.js'i bir CDN'den dahil edin ve grafiklerinizi C# modelinizden serileştirilen veriyle yapılandırın:

@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'te oluşturulan içerik içeren sayfaları işlerken, betiklerin tamamlamasını beklemek için işleyiciyi yapılandırın:

: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

Örnek Çıktı

Koşullu Formatlama ve İş Mantığı Uygulama

Envanter raporları, hemen aksiyon gerektiren maddelere dikkat çeken görsel göstergelere fayda sağlar. Kullanıcıları sorunları bulmak için yüzlerce satır arasında gezinmeye zorlamak yerine, koşullu formatlama, istisnaları görsel olarak belirgin hale getirir. Veri değerlerine dayalı CSS sınıflarını uygulamak için Razor'un satır içi ifadelerini kullanın:


@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

Örnek Çıktı

Alt Raporlar ve Bölüm Araları Oluşturma

Bağımsız olarak oluşturulmuş raporları bir belge içinde birleştirmek için IronPDF'in birleştirme işlevselliğini kullanın:

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

Örnek Çıktı

İçindekiler Tablosu Oluşturma

IronPDF, HTML'nizdeki başlık elementlerine dayalı olarak otomatik içindekiler tablosu oluşturabilir:

: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

Crystal Reports'tan IronPDF'e Geçiş

Mevcut bir raporlama sistemini taşımak, aksaklığı en aza indirmek ve modernleştirip basit hale getirme fırsatını yakalamak için dikkatli planlama gerektirir. Crystal Reports kavramlarının, her özelliği bire bir kopyalamaya veya orijinal raporların her garipliğini korumaya çalışmak yerine, HTML tabanlı yaklaşıma nasıl haritalandığını anlamak, daha hızlı hareket etmenizi sağlayacaktır.

Crystal Reports Kavramlarını IronPDF'e Haritalayın

Kavramsal haritayı anlamak, mevcut raporları sistematik bir şekilde çevirmenize yardımcı olur:

Crystal Reports IronPDF Eşdeğeri
Rapor bölümleri CSS sayfa-kırılma özelliklerine sahip HTML div'ler
Parametre alanları Razor görünümlerine geçirilen model özellikleri
Formül alanları Model sınıflarında C# hesaplanmış özellikler
Koşan toplamlar LINQ toplama işlemleri
Alt raporlar Kısmi görünümler veya birleştirilmiş PDF belgeleri
Gruplama/sıralama Veriyi şablona geçmeden önce LINQ işlemleri
Çapraz tablo raporları İç içe döngüler kullanarak HTML tabloları
Koşullu biçimlendirme CSS sınıfları ile Razor @if blokları

.rpt Şablonlarını Dönüştürmek için En İyi Strateji

.rpt dosyalarını programlı olarak ayrıştırmaya çalışmayın. Bunun yerine, mevcut PDF çıktıları görsel spesifikasyonlar olarak kullanarak, sistematik dört aşamalı bir strateji ile mantığı yeniden inşa edin:

  1. Envanter: Tüm .rpt dosyalarını amaçları, veri kaynakları ve kullanım sıklığı ile kataloglayın. Gereksiz raporları kaldırarak geçiş kapsamını azaltın.

  2. Önceliklendirme: Yüksek sıklıklı raporları öncelikle geçirin. Basit yerleşim düzenlerine sahip veya sürekli bakım sorunları olan raporları hedefleyin.

  3. Referans: Mevcut Crystal Reports'u PDF olarak dışa aktarın. Geliştiricilerin eşleşmesi için bunları görsel spesifikasyonlar olarak kullanın.

  4. Doğrulama: Üretim veri hacimleriyle test edin. 10 satır ile anında renderlanan şablonlar, 10.000 satır ile yavaşlayabilir.

.NET'te Toplu Rapor Oluşturma ve Zamanlama

Üretim sistemleri genellikle birçok raporu aynı anda oluşturmak veya zamanlanmış rapor işleri çalıştırmak zorunda kalır. IronPDF'nin iş parçacığı güvenli tasarımı her iki senaryoyu da verimli bir şekilde destekler.

Birden Fazla Raporu Paralel Olarak Oluşturun

Batch işleme için, Parallel.ForEachAsync veya Task.WhenAll ile async desenlerini kullanın:

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

Örnek Çıktı

Toplu işleme örneği, birden fazla faturayı paralel olarak oluşturur. İşte oluşturulan toplu faturaların bir örneği:

Rapor Oluşturma İşlevselliğini ASP.NET Core Arka Plan Hizmetleri ile Entegre Edin

Zamanlanmış rapor oluşturma, ASP.NET Core'un barındırılan hizmet altyapısına doğal olarak uyum sağlar:

// 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

Tam Test Projesini İndirin

Bu öğreticinin tüm kod örnekleri, çalışmaya hazır bir .NET 10 test projesinde mevcuttur. indirme, tam kaynak kodu, veri modelleri, HTML şablonları ve yukarıda gösterilen tüm örnek PDF'leri oluşturacak bir test çalıştırıcısı içerir.

Sonraki Adımlar

Bu kılavuzdaki örnekler, IronPDF'nin işletme raporlama ihtiyaçlarının tüm yelpazesini ele aldığını göstermektedir: satır ögeleri ve toplamlar ile basit faturalar, gruplandırılmış veri ve fotoğraflarla karmaşık çalışan dizinleri, koşullu biçimlendirme ve grafiklerle envanter raporları, yüzlerce belgenin toplu işlenmesi, ve arka plan hizmetleri aracılığıyla zamanlanmış oluşturma.

Mevcut bir Crystal Reports uygulaması için alternatifler değerlendiriyorsanız, öncelikle yüksek değerli bir raporla başlayın. Bu kılavuzda gösterilen HTML-to-PDF kalıplarını kullanarak yeniden oluşturun, geliştirme deneyimini ve çıktı kalitesini karşılaştırın, ardından buradan genişletin. Birçok ekip, ilk dönüştürülen raporlarının birkaç saat sürdüğünü, çünkü kalıpları ve temel şablonları kurarken, sonraki raporların yalnızca dakikalar içinde toplandığını fark eder, çünkü Razor şablonlarını ve stil öğelerini yeniden kullanırlar. Yerleşim hassasiyeti için, piksel mükemmelliğinde render kılavuzu, CSS ile Crystal Reports çıktısı ile tam uyum sağlama yöntemlerini kapsar.

Yapmaya hazır mısınız? IronPDF İndir ve ücretsiz deneme ile deneyin. Aynı kütüphane, hem tek rapor renderinden yüksek hacimli toplu oluşturmaya kadar .NET ortamlarında her şeyi ele alır. Raporları taşımak hakkında sorularınız varsa veya mimari rehberliğe ihtiyaçınız varsa, mühendislik destek ekibimize ulaşın.

Sıkça Sorulan Sorular

IronPDF nedir?

IronPDF, geliştiricilerin programatik olarak PDF belgeleri oluşturmasını, düzenlemesini ve oluşturmasını sağlayan bir C# kütüphanesidir, Crystal Reports gibi geleneksel raporlama araçlarına modern bir alternatif sunar.

IronPDF Crystal Reports alternatifi olarak nasıl hizmet eder?

IronPDF, geliştiricilerin HTML/CSS şablonlarını kullanmasına izin verir, bu da kolayca stillendirilip değiştirilebilen esnek ve modern bir rapor oluşturma yaklaşımı sunar, Crystal Reports'un daha katı yapısına karşılık.

IronPDF kullanarak faturalar oluşturabilir miyim?

Evet, IronPDF ile HTML/CSS şablonlarını kullanarak detaylı ve özelleştirilmiş faturalar oluşturabilir, belgeleri profesyonel görünümlü bir şekilde tasarlamak kolaylaşır.

IronPDF kullanarak çalışan dizinleri oluşturmak mümkün mü?

Kesinlikle. IronPDF, dinamik veriler ve HTML/CSS kullanarak kapsamlı çalışan dizinleri oluşturarak net ve organize bir sunum sağlar.

IronPDF envanter raporlarında nasıl yardımcı olabilir?

IronPDF, HTML/CSS şablonları kullanarak envanter raporu oluşturmayı kolaylaştırır, bu da verileri dinamik olarak doldurarak güncel ve görsel açıdan hoş raporlar sunar.

IronPDF'de HTML/CSS şablonları kullanmanın avantajları nelerdir?

IronPDF'de HTML/CSS şablonları kullanmak tasarımda esneklik, kolay güncelleme ve web teknolojileriyle uyumluluk sunar, bu da rapor tasarımlarını sürdürmeyi ve geliştirmeyi kolaylaştırır.

IronPDF .NET 10'u destekliyor mu?

Evet, IronPDF .NET 10 ile uyumludur, geliştiricilerin rapor oluşturma ihtiyaçları için en son .NET özelliklerini ve iyileştirmelerini kullanabilmesini sağlar.

IronPDF rapor oluşturma hızını nasıl artırır?

IronPDF performans için optimize edilmiştir, bu da HTML/CSS işlemlerini verimli bir şekilde işleyip yüksek kaliteli PDF belgelerine dönüştürerek raporları hızlıca oluşturmasını sağlar.

Curtis Chau
Teknik Yazar

Curtis Chau, Bilgisayar Bilimleri alanında lisans derecesine sahiptir (Carleton Üniversitesi) ve Node.js, TypeScript, JavaScript ve React konularında uzmanlaşmış ön uç geliştirme üzerine uzmanlaşmıştır. Kullanıcı dostu ve estetik açıdan hoş arayüzler tasarlamaya tutkuyla bağlı olan Curtis, modern çerç...

Daha Fazlasını Oku
Başlamaya Hazır mısınız?
Nuget Indirmeler 18,332,619 | Sürüm: 2026.4 yeni yayınlandı
Still Scrolling Icon

Hala Kaydiriyor musunuz?

Hızlı bir kanit mi istiyorsunuz? PM > Install-Package IronPdf
bir örnek çalıştır HTML'nizin PDF olduğunu izleyin.