Blog
Mert Özen'in yazılım geliştirme, backend, frontend ve DevOps üzerine blog yazıları.
.NET'te Stream Modeli: Read'in Az Byte Dönmesi, Decorator Zinciri ve leaveOpen
Stream.Read istediğin kadar değil, o an hazır olan kadar byte döner; sabit uzunluklu veri için ReadExactly kullanmak gerekir. Bookstore sipariş export'unu FileStream, GZipStream ve CryptoStream zinciriyle yazıp okuyor; dispose sırasını, leaveOpen'ı, ArrayPool ile kopyalamayı ve kendi Stream'ini yazmayı gerçek çıktılarla gösteriyoruz.
FileStream ve Async File I/O: FileStreamOptions, RandomAccess ve Atomik Yazma
Bir dosyayı yarım kalmadan değiştirmenin yolu, içeriği aynı klasörde geçici bir dosyaya yazıp Flush(flushToDisk: true) ile diske indirmek ve File.Move ile tek adımda yerine koymaktır. FileStreamOptions'ı, Windows ve Linux'ta async I/O farkını, RandomAccess ile offset tabanlı okumayı, CreateNew ile race'siz oluşturmayı ve FileSystemWatcher'ın kaçırdıklarını yakalayan bir Worker'ı Bookstore örnekleriyle gösteriyoruz.
System.IO.Pipelines: PipeReader, PipeWriter ve ReadOnlySequence ile Kopyasız Parse
System.IO.Pipelines, satırların iki buffer'a bölünmesi, buffer büyütme ve kopyalama gibi işleri üstlenip veriyi string'e çevirmeden parse etmeni sağlar; doğru kullanımın anahtarı AdvanceTo(consumed, examined) kuralıdır. Bookstore'un NDJSON sipariş akışını PipeReader ile parse edip Channel'a aktarıyor, backpressure'ı ve StreamReader ile farkı BenchmarkDotNet ile gerçek sayılarla gösteriyoruz.
System.Text.Json Derinlemesine 1: Options, Converter'lar, Polymorphism ve Strict Mode
System.Text.Json varsayılan olarak duplicate property'de son değeri alır, eksik ve bilinmeyen alanları sessizce geçer; dışarıdan gelen payload'larda .NET 10'un JsonSerializerOptions.Strict ve AllowDuplicateProperties = false ayarlarıyla bunu kapatmak gerekir. Web varsayılanlarını, naming policy'leri, required ve nullable kurallarını, Bookstore'un PaymentMethod hiyerarşisiyle polymorphism'i, Money converter'ını ve contract customization'ı gerçek çıktılarla gösteriyoruz.
System.Text.Json Derinlemesine 2: Source Generation, JsonNode/Utf8JsonReader ve Büyük JSON'u Streaming İşlemek
Büyük bir JSON export'unu List<T>'ye deserialize etmek heap'i dosya boyutunun iki katının üstüne çıkarır; DeserializeAsyncEnumerable ile aynı iş sabit ve küçük bir bellekle biter. 1 milyon siparişlik Bookstore export'unda bunu ölçüyor, source generation'ın başlangıç ve fast path kazancını, Native AOT'taki davranışını, JsonDocument/JsonNode/Utf8JsonReader seçimini, JsonSchemaExporter'ı ve Newtonsoft.Json'dan geçişi gösteriyoruz.
XML ile Çalışmak: XmlReader, LINQ to XML, XmlSerializer ve Güvenli Varsayılanlar
Büyük XML'i XmlReader ile gezip her belgeyi kısa ömürlü bir XElement'e çeviren hibrit okuma, LINQ to XML'in rahatlığını sabit bellekle birleştirir: 267 MB'lık dosyada XDocument 703 MB, hibrit yöntem 8,5 MB heap kullandı. UBL benzeri bir Bookstore faturasıyla namespace'leri, XSD doğrulamayı, XmlSerializer ile DataContractSerializer farkını, XmlSerializer'ın cache'lenmeyen constructor'larını ve XXE'ye karşı .NET'in varsayılanlarını gösteriyoruz.
CSV ile Çalışmak: RFC 4180, Türkçe Excel, Encoding ve CSV Injection
CSV'yi string.Split ile değil, RFC 4180'in tırnak kurallarını bilen bir okuyucuyla ve her sayıyı açık bir culture ile parse etmek gerekir; tr-TR sunucuda decimal.Parse("1234.50") sessizce 123450 döner. Windows-1254 kodlu bir banka dosyasını CsvHelper 34 ile hatalı satırda durmadan import ediyor, Türkçe Excel'de doğru açılan export'u, CSV injection korumasını ve CsvHelper, Sep, Sylvan ölçümünü gösteriyoruz.
Compression ve Arşivler: GZip, Brotli, ZipArchive'ın Async API'leri ve Zip Slip
Büyük bir zip'i kullanıcıya göndermek için dosya ya da MemoryStream gerekmez: ZipArchive'ın Create modu entry'leri doğrudan response body'ye yazar, ama .NET 10'un async ZIP API'leri her entry'nin sonunu hâlâ senkron yazdığı için Kestrel'de buna ayrıca izin vermek gerekir. GZip, ZLib, Deflate ve Brotli'yi seviyeleriyle ölçüyor, zip ile tar.gz'yi karşılaştırıyor ve yüklenen zip'i Zip Slip ve zip bomb'a karşı güvenle açıyoruz.
Büyük Dosyalar ve Memory-Mapped File'lar: Sabit Bellekle Gigabyte'larca Veri İşlemek
Büyük bir dosyayı sabit bellekle işlemek yetmez, allocation'ı da sıfıra indirmek gerekir: 5 GB'lık sipariş log'unda File.ReadLines 54 MB bellekle ama 35 GB allocation ile 14,5 saniye sürerken, kiralık buffer ve byte parser ile aynı iş 2,5 saniyede bitti. Dosyayı satır sınırına hizalı offset aralıklarına bölüp Channel ile paralel işlemeyi, memory-mapped file'ın ne zaman hızlandırdığını ve working set'e etkisini gerçek ölçümlerle gösteriyoruz.
.NET 6'dan .NET 10'a I/O ve Serialization'da Gelenler
.NET 6'dan .NET 10'a I/O ve serialization'da gelenlerin çoğu, projelerde elle yazılmış döngülerin, naming policy'lerin ve kontrollerin yerini alan hazır API'ler: RandomAccess, ReadExactly, File.ReadLinesAsync, SnakeCaseLower, JsonUnmappedMemberHandling, JsonSerializerOptions.Web ve Strict. Her sürümün yeniliklerini çalışan önce ve sonra örnekleriyle ve yükseltmede davranışı sessizce değişen noktalarla topluyoruz.
.NET 11 ile I/O ve Serialization: Union Serialization, JSON Lines, In-Memory Stream'ler ve Zstandard
.NET 11'de System.Text.Json C# 15 union'larını sarmalayıcı olmadan serialize ediyor ve SerializeAsyncEnumerable ile JSON Lines yazıyor; System.IO.Compression'a Zstandard, ZIP'e parola ve CRC32 doğrulaması geldi. Yeni in-memory stream'leri, closed hierarchy polymorphism'ini ve eski testleri kırabilecek iki davranış değişikliğini (boş GZip artık 20 byte, bozuk zip entry'si artık exception) .NET 10 ile karşılaştırarak gösteriyoruz.