{:} RenderTemplate
Açık kaynak · .NET · MIT Lisansı

Basit, hızlı ve deterministik
bir şablon motoru.

DSO.Core.RenderTemplate, POCO nesnelerinizi ve koleksiyonlarınızı; Razor gibi ağır bir view katmanı kurmadan, öngörülebilir sırayla HTML veya düz metne dönüştüren hafif bir .NET kütüphanesidir. Embed kompozisyonu, pipe tabanlı filtre zinciri, derlenmiş dotted-path erişimi ve varsayılan HTML kaçışı ile üretime hazır çıktı üretir.

Lisans · MIT Dil · C# 10+ Bağımlılık · Yok Thread-safety · Örnek başına değil

Bir veri tokenının anatomisi

{:Price|currency:tr-TR}
{ … }Token sınırlayıcıları
:Veri tokenı işareti (embed'den ayırır)
PriceKolon adı / dotted path
currencyUygulanan filtre
tr-TRFiltre argümanı (kültür kodu)

Bölüm 01

Amaç & Kapsam

Bu bölüm, kütüphanenin neden var olduğunu, hangi problemi çözmeyi hedeflediğini ve sınırlarının nerede başlayıp nerede bittiğini tüm detaylarıyla açıklar.

Amaç

DSO.Core.RenderTemplate; POCO nesneleri veya nesne koleksiyonlarını, tam bir view engine kurmadan hızlıca HTML tablosuna, e-posta gövdesine, rapor metnine veya düz metin çıktıya dönüştürmek amacıyla tasarlanmıştır. Kütüphanenin tasarım felsefesi beş temel hedef üzerine kuruludur:

  • Öngörülebilirlik: Aynı şablon ve aynı veri her zaman aynı çıktıyı üretir; embed'ler eklenme sırasına göre çözülür, veri tokenları akış sırasında yazılır, döngüler cycle guard ile engellenir.
  • Performans: Dotted-path erişimleri şablon seti başına bir kez System.Linq.Expressions ile derlenir; satır başına reflection maliyeti oluşmaz. Bu, yüzlerce/binlerce satırlık tabloların render edilmesi gereken raporlama senaryolarında ölçülebilir fark yaratır.
  • Hafiflik: Harici hiçbir pakete bağımlı değildir. Razor, Handlebars.NET veya Scriban gibi tam teşekküllü template engine'lerin getirdiği derleme adımı, view lokasyonu yönetimi veya sandbox karmaşıklığı yoktur.
  • Güvenli varsayılanlar: Tüm veri tokenları varsayılan olarak HTML olarak kaçışlanır; geliştirici bilinçli olarak |raw demeden ham HTML enjekte edilemez. Bu, kullanıcı girdisinden türeyen verilerin şablonlara basılması sırasında XSS riskini azaltır.
  • Genişletilebilirlik: IFilter arayüzü ile yeni filtreler kayıt edilebilir; eksik şablon/kolon davranışı politika (Throw / KeepToken / Empty) ile yapılandırılabilir.

Kısacası kütüphane, "POCO listesini bir HTML tablosuna dök" ya da "bu nesneden düz metin bir e-posta gövdesi üret" gibi tekrar eden ihtiyaçları, Razor taşımadan, minimum yüzey alanıyla çözmeyi hedefler.

Hedef Kitle & Tipik Senaryolar

Kütüphane özellikle şu profildeki geliştiriciler ve senaryolar için uygundur:

  • Sunucu tarafında POCO/DTO listelerinden HTML rapor veya tablo üreten backend servisleri.
  • Zamanlanmış görevlerde (job/worker) e-posta gövdesi veya bildirim metni oluşturan sistemler.
  • PDF üretiminden önce ara HTML çıktısı hazırlayan pipeline'lar.
  • Log, dışa aktarma (export) veya şablon tabanlı metin üretimi yapan konsol/servis uygulamaları.
  • Razor view engine'inin ek yükünü (derleme, view lokasyon çözümleme) istemeyen mikroservisler.

Kapsam içinde

  • Embed (kısmi şablon) kaydı ve çözümleme
  • Veri tokenı okuma: {:Kolon} ve dotted-path {:A.B.C}
  • Pipe tabanlı, zincirlenebilir filtre sistemi ve yerleşik filtreler
  • Kültüre duyarlı tarih / sayı / para birimi biçimlendirme
  • Eksik şablon/kolon için yapılandırılabilir politika
  • Tekli nesne ve koleksiyon render kalıpları
  • RenderTemplateQuick ile tek satırlık kullanım API'si
  • Varsayılan HTML kaçışı ve kontrollü bypass (raw, nl2br)

Kapsam dışında

  • Şablon içinde koşul/döngü sözdizimi (if/for gibi) — döngüler C# tarafında, AddTemplateForEach ile yönetilir
  • Literal { / } kaçışı (yol haritasında)
  • Bölüm/loop yardımcı sözdizimi (yol haritasında)
  • Engine örneğinin thread-safe olması — eşzamanlı işler için ayrı örnek gerekir
  • Sunucu tarafı görünüm (view) yönlendirme, layout/master page kavramı
  • Resmi bir NuGet paketi olarak yayınlanmış olmak (şu an için proje referansı/kaynak dahil önerilir)

Bölüm 02

Neden RenderTemplate?

Render süreci öngörülebilir, kodu ise hafif ve hızlı olmalıdır. Bu motor, ağır view katmanları yerine küçük, deterministik bir çekirdek kullanır:

  • İki geçişli render: önce embed (kısmi) çözülür, ardından veri tokenları akışta yazılır.
  • Dotted-path erişimleri için önceden derlenen expression accessor'lar — satır başına reflection yok.
  • Deterministik çıktı sırası ve embed'ler için döngü koruması (cycle guard).
  • Varsayılan HTML kaçışı ile güvenli çıktı; gerektiğinde raw filtresiyle bypass.

POCO listelerini tablolaştırmak ya da Razor taşımadan düz metin/HTML raporlar üretmek gerektiğinde idealdir.

Bölüm 03

Özellikler

⚙

Deterministik sıra

Eklenme sırasına göre render + cycle guard ile embed döngü koruması.

▤

Embed kompozisyonu

{thead}, {tbody} gibi alt şablonları kısmi olarak birleştirin.

:

Data token

{:Name} ve filtre zinciri: {:Price|currency:tr-TR}.

→

Dotted path

{:User.Name} — ekleme anında derlenir, satır başına reflection yok.

⟲

Cycle guard

Şablon döngülerini yakalar ve net bir istisna fırlatır.

§

Politikalar

Eksik şablon/kolon için Throw, KeepToken, Empty.

▮

Filtre altyapısı

upper, lower, trim, raw, date, number, currency, yesno, truncate, nl2br.

⚡

Bağımlılıksız

Harici pakete ihtiyaç duymadan hızlı çalışır.

✦

Minimal sözdizimi

{Name} embed, {:Col|filters} veri.

🌍

Kültür duyarlılığı

Özelleştirilebilir varsayılan kültür ile biçimlendirme.

✎

Özel filtreler

IFilter uygulayıp kendi filtrenizi kayıt edin.

▸

Quick yardımcılar

Tek satırda render için RenderTemplateQuick API'si.

Bölüm 04

Kurulum

Projeyi referans olarak ekleyin veya kaynak dosyaları çözümünüze dahil edin.

using DSO.Core.RenderTemplate;

Not: NuGet paketi olarak henüz yayınlanmamıştır; şu an için proje referansı ya da kaynak dosyaların doğrudan çözüme dahil edilmesi önerilir.

Bölüm 05

Hızlı Başlangıç

var items = new[]
{
    new { Name = "Kahve", Price = 145.75m, InStock = true,  Added = new DateTime(2025, 9, 1) },
    new { Name = "Çay",   Price =  75.00m, InStock = false, Added = new DateTime(2025, 9, 2) },
};

string rowTpl = "<tr><td>{:Name|upper}</td><td>{:Price|currency:tr-TR}</td><td>{:InStock|yesno:Evet,Hayır}</td><td>{:Added|date:yyyy-MM-dd}</td></tr>";
string tableTpl = "<table class=\"tbl\">{thead}{rows}{tfoot}</table>"; // embed'ler

var eng = new RenderTemplate(new RenderTemplateSettings { HtmlEncodeByDefault = true });

// Kısmi şablonlar / embeds
eng.AddTemplate("thead", "<thead><tr><th>Ürün</th><th>Fiyat</th><th>Stok</th><th>Eklendi</th></tr></thead>");
eng.AddTemplate("rows",  rowTpl);
eng.AddTemplate("tfoot", "<tfoot><tr><td colspan=\"4\">Toplam: {:Price|number:#,##0.00}</td></tr></tfoot>");

// Veri tokenı içeren şablona veriyi bağla
eng.AddTemplateForEach("rows", rowTpl, items);
eng.AddTemplate("__root__", tableTpl);

string html = eng.Render("__root__");

Çıktı (gösterim amacıyla biçimlendirilmiştir):

<table class="tbl">
  <thead>...</thead>
  <tr><td>KAHVE</td><td>₺145,75</td><td>Evet</td><td>2025-09-01</td></tr>
  <tr><td>ÇAY</td>  <td>₺75,00</td><td>Hayır</td><td>2025-09-02</td></tr>
  <tfoot>...</tfoot>
</table>

Bölüm 06

Şablon Sözdizimi

Şablon dili yalnızca iki token türü tanır: embed tokenları ve veri tokenları. Aşağıdaki alt bölümler her ikisini de detaylandırır.

Embed tokenları {Name}

Adıyla kayıt ettiğiniz başka bir şablonu (partial) referanslar:

{Header}
<ul>
  {Rows}
</ul>
{Footer}
  • Eksik embed davranışı RenderTemplateSettings.OnMissingTemplate politikasına bağlıdır.
  • Embed çözümü veri tokenlarından önce yapılır (bkz. Performans Notları).

Veri tokenları {:Col|f|f:arg}

Geçerli satır/nesneden bir kolon/özellik okur ve isteğe bağlı pipe filtreleri uygular:

{:Name}
{:Price|currency:tr-TR}
{:CreatedAt|date:yyyy-MM-dd}
{:Comment|trim|truncate(40, ...)}
{:RawHtml|raw}
  • Kolon adı ilk : sonrası kısımdır.
  • Filtre argümanları : veya ( ) ile verilebilir: date:yyyy ya da date(yyyy).
  • Geçersiz kolonlar OnMissingColumn politikasına uyar.

Dotted path {:Customer.Address.City}

İç içe özelliklere satır başına reflection yapmadan erişim sağlar. Motor, şablondan dotted path'leri çıkarır ve bir kez expression ile accessor derler:

var order = new {
  Id = 42,
  Customer = new { Name = "Ada", Address = new { City = "İzmir", Country = "TR" } },
};

string tpl = "Sipariş {:Id} — Müşteri {:Customer.Name} ({:Customer.Address.City})";
var result = RenderTemplateQuick.RenderOne(tpl, order);
// => "Sipariş 42 — Müşteri Ada (İzmir)"

Dotted path hatalı biçimlendirilmişse veya derlenemezse eksik kolon politikası devreye girer.

Filtreler (yerleşikler)

FiltreAçıklama
upperMetni büyük harfe çevirir.
lowerMetni küçük harfe çevirir.
trimBaştaki/sondaki boşlukları temizler.
rawHTML kaçışını bu token için devre dışı bırakır.
dateTarihi belirtilen formatla biçimlendirir, örn. date:yyyy-MM-dd.
numberSayıyı belirtilen sayı formatıyla biçimlendirir.
currencyPara birimi olarak biçimlendirir, kültür argümanı alır.
yesnoBoolean değeri özelleştirilebilir metinlere çevirir, örn. yesno:Evet,Hayır.
truncateMetni belirli uzunlukta keser ve isteğe bağlı sonek ekler.
nl2brSatır sonlarını <br/> etiketine çevirir; önce encode eder.

Kendi filtrenizi de yazabilirsiniz; bkz. Özel Filtreler Yazma.

Bölüm 07

Kullanım Kalıpları

Tek nesne

var settings = new RenderTemplateSettings { DefaultCulture = CultureInfo.GetCultureInfo("tr-TR") };
string tpl = "Merhaba {:Name}, bakiyeniz {:Balance|currency}.";
var data = new { Name = "Ethem", Balance = 1234.5m };

string text = RenderTemplateQuick.RenderOne(tpl, data, settings);

Koleksiyonlar

var rows = new []
{
    new { Name = "A", Score = 98.5 },
    new { Name = "B", Score = 76.3 },
};

string rowTpl = "<li>{:Name} — {:Score|number:#0.0}</li>";
string html = rows.RenderWith(rowTpl);

Kısmi/Embed kayıt etmek

string page = RenderTemplateQuick.RenderMany(
    template: "<ul>{Rows}</ul>",
    data: rows,
    setup: eng =>
    {
        eng.AddTemplate("Rows", "<li>{:Name}</li>");
        eng.AddTemplateForEach("Rows", "<li>{:Name}</li>", rows);
    });

Tek satır yardımcılar (Quick)

  • RenderTemplateQuick.RenderLiteral(template[, settings])
  • RenderTemplateQuick.RenderOne(template, data[, setup][, settings])
  • RenderTemplateQuick.RenderMany(template, data[, setup][, settings])
  • Enumerable extension: data.RenderWith(template[, setup][, settings])

Alt tarafta kısa ömürlü bir engine oluşturur ve __root__'u render eder.

Bölüm 08

Ayarlar & Politikalar

var settings = new RenderTemplateSettings
{
    OnMissingTemplate   = OnMissingTemplatePolicy.Throw,   // KeepToken / Empty
    OnMissingColumn     = OnMissingColumnPolicy.Throw,     // KeepToken / Empty
    DefaultCulture      = CultureInfo.InvariantCulture,
    NameComparer        = StringComparer.Ordinal,
    HtmlEncodeByDefault = true,
};
ThrowKeepTokenEmpty
AyarAçıklama
OnMissingTemplate{Embed} bulunamazsa ne yapılacağını belirler.
OnMissingColumnVeri kolonu/yolu okunamazsa ne yapılacağını belirler.
HtmlEncodeByDefault& < > " ' karakterlerini encode eder; bir filtre rawBypass işaretlerse atlanır (örn. raw, nl2br).
DefaultCulturedate/number/currency filtreleri için kullanılır.
NameComparerŞablon adı eşleşmesinin büyük/küçük harf duyarlılığını belirler.

Bölüm 09

Özel Filtreler Yazma

IFilter uygulayıp kayıt edin:

public sealed class SlugFilter : IFilter
{
    public string Name => "slug";
    public string Apply(string input, string? arg, CultureInfo culture, ref bool rawBypass)
    {
        var s = input ?? string.Empty;
        s = s.Trim().ToLower(culture);
        var sb = new StringBuilder(s.Length);
        foreach (var ch in s)
            sb.Append(char.IsLetterOrDigit(ch) ? ch : '-');
        return sb.ToString().Trim('-');
    }
}

var settings = new RenderTemplateSettings();
settings.Filters.Register(new SlugFilter());

Sonra {:Title|slug} olarak kullanın.

Bölüm 10

Performans Notları

  • İki geçişli hat: önce embed geçişi {Name} tokenlarını çözer ve düz bir şablon dizesi elde eder; ardından veri geçişi her satır için akışta yazar, sadece {:...} yerleri değiştirilir.
  • Dotted-path accessor'lar şablon seti başına bir kez System.Linq.Expressions ile derlenir ve satırlar arasında paylaşılır.
  • Dotted path için satır başına reflection yoktur; basit kolonlar hızlı tablo okuyucusu ile alınır.
  • Cycle guard ile embed döngülerine izin verilmez; net bir istisna fırlatılır.
  • Çıktı deterministiktir: embed ile içerden basılmış olanlar kökte tekrar basılmaz; ekleme sırası korunur.
  • Engine örneği thread-safe değildir; eşzamanlı işler için ayrı örnek oluşturun.

Bölüm 11

Güvenlik / HTML Kaçış

  • Varsayılan olarak her veri tokenı HTML-encode edilir (& < > " ').
  • Sadece ilgili tokenı atlamak için |raw kullanın.
  • nl2br önce encode eder, sonra satır sonlarını <br/> yapar ve dış encode'u bypass eder.

Bölüm 12

API Referansı

RenderTemplate

  • AddTemplate(string name, string template)
  • AddTemplateForEach<T>(string name, string template, IEnumerable<T>? data)
  • AddTemplateFor<T>(string name, string template, T data)
  • string Render(string? root = null)
  • void RenderTo(StringBuilder sb, string? root = null) / void RenderTo(TextWriter writer, string? root = null)
  • RenderTemplateSettings Settings { get; }

RenderTemplateSettings

OnMissingTemplate · OnMissingColumn · DefaultCulture · NameComparer · HtmlEncodeByDefault · FilterRegistry Filters

RenderTemplateQuick

  • RenderLiteral(string template[, Action<RenderTemplate> setup][, RenderTemplateSettings settings])
  • RenderOne<T>(string template, T data[, Action<RenderTemplate> setup][, RenderTemplateSettings settings])
  • RenderMany<T>(string template, IEnumerable<T> data[, Action<RenderTemplate> setup][, RenderTemplateSettings settings])

RenderTemplateQuickExtensions

  • string RenderWith<T>(this IEnumerable<T> data, string template[, Action<RenderTemplate> setup][, RenderTemplateSettings settings])

Bölüm 13

SSS

Literal içinde { veya } nasıl kaçarım?

Henüz literal kaçışı desteklenmiyor; isterseniz string'i bölün veya embed kullanın.

HTML encode olmadan render edebilir miyim?

Global olarak HtmlEncodeByDefault = false yapın ya da ilgili token için |raw / |nl2br kullanın.

StrongType / Free modu nasıl seçiliyor?

AddTemplateForEach<T> çağrısında T bir referans tipi (string hariç) ise tablo StrongType çalışır ve public field/property'leri kolon yapar. Değer tipleri/string için Free moda düşer ve tek kolon (0) kabul edilir.

Bölüm 14

Yol Haritası

Literal kaçışı{{ / }} ile literal süslü parantez desteği.
Bölüm/loop yardımcılarıŞablon sözdiziminde döngü/bölüm ifadeleri.
Daha güvenli null/format işlemeÖzel filtrelerde geliştirilmiş null ve format toleransı.

Bölüm 15

Lisans

Bu proje MIT Lisansı ile lisanslanmıştır. Ayrıntılar için depodaki LICENSE dosyasına bakın.