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.
Bir veri tokenının anatomisi
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.Expressionsile 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
|rawdemeden ham HTML enjekte edilemez. Bu, kullanıcı girdisinden türeyen verilerin şablonlara basılması sırasında XSS riskini azaltır. - Genişletilebilirlik:
IFilterarayü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ı
RenderTemplateQuickile 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,
AddTemplateForEachile 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
rawfiltresiyle 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.OnMissingTemplatepolitikası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:yyyyya dadate(yyyy). - Geçersiz kolonlar
OnMissingColumnpolitikası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)
| Filtre | Açıklama |
|---|---|
upper | Metni büyük harfe çevirir. |
lower | Metni küçük harfe çevirir. |
trim | Baştaki/sondaki boşlukları temizler. |
raw | HTML kaçışını bu token için devre dışı bırakır. |
date | Tarihi belirtilen formatla biçimlendirir, örn. date:yyyy-MM-dd. |
number | Sayıyı belirtilen sayı formatıyla biçimlendirir. |
currency | Para birimi olarak biçimlendirir, kültür argümanı alır. |
yesno | Boolean değeri özelleştirilebilir metinlere çevirir, örn. yesno:Evet,Hayır. |
truncate | Metni belirli uzunlukta keser ve isteğe bağlı sonek ekler. |
nl2br | Satı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,
};
| Ayar | Açıklama |
|---|---|
OnMissingTemplate | {Embed} bulunamazsa ne yapılacağını belirler. |
OnMissingColumn | Veri kolonu/yolu okunamazsa ne yapılacağını belirler. |
HtmlEncodeByDefault | & < > " ' karakterlerini encode eder; bir filtre rawBypass işaretlerse atlanır (örn. raw, nl2br). |
DefaultCulture | date/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.Expressionsile 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
|rawkullanı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ı
{{ / }} ile literal süslü parantez desteği.Bölüm 15
Lisans
LICENSE dosyasına bakın.