2 puan yazan GN⁺ 2024-10-20 | 1 yorum | WhatsApp'ta paylaş

-.NET 9.0, çeşitli genel LINQ senaryolarında .NET 8’e kıyasla çalışma süresini belirgin biçimde azaltıyor ve bazı benchmark’larda atamaları da ortadan kaldırıyor

  • Diziler veya List<T> üzerinde dolaşırken TryGetSpan() ile ReadOnlySpan<T> elde edip yineleme maliyetini düşürmek, temel iyileştirmelerden biri
  • TryGetSpan(), TSource[] ve List<TSource> türlerini tür karşılaştırmasıyla ayırt ediyor; ancak List<T> içindeki diziyi span olarak alma yöntemi, kapasite değiştiğinde geçersiz hale gelebilen bir Unsafe ailesi optimizasyonu -.NET 9’daki LINQ, yaygın çağrı zincirlerini tanıyıp özel iterator’lar oluşturuyor ve Count(), First(), Last(), ElementAt(), Sum() gibi sonlandırıcı metotlarda ek optimizasyonlar uyguluyor
  • Yalnızca basit bir migration ve yeniden derleme ile bazı LINQ performans iyileştirmeleri elde edilebiliyor; ayrıca SIMD kullanımı ve boş sequence’leri erken algılama gibi optimizasyonlar da buna dahil

Dizi ve liste dolaşımı neden hızlandı?

  • İlk benchmark, Enumerable.Range(1, 10_000).ToArray() sonucunu IEnumerable<int> olarak tutup ardından Count, All, Any, First, Single, Last çalıştırarak .NET 8 ile .NET 9’u karşılaştırıyor
  • BenchmarkDotNet kullanılıyor; proje net8.0;net9.0 hedeflemeli ve Release modunda derlenmeli
  • .NET 9’da birçok metodun çalışma süresi ciddi biçimde azalıyor ve atamalar da ortadan kalkıyor
    • LinqCount: 16,198.490 ns3,043.563 ns, 32 B atama → atama yok
    • LinqAny: 17,096.735 ns2,483.927 ns, 32 B atama → atama yok
    • LinqFirst: 15,289.747 ns2,243.341 ns, 32 B atama → atama yok
    • LinqSingle: 21,684.114 ns4,884.329 ns, 32 B atama → atama yok
    • LinqAll: 10.588 ns2.562 ns, 32 B atama → atama yok
    • LinqLast: 15.967 ns6.918 ns

TryGetSpan() farkı nasıl yaratıyor?

  • Performans artışının ana nedenlerinden biri TryGetSpan() kullanımı
  • Dolaşılan enumerable bir dizi veya listeyse, TryGetSpan() daha hızlı yineleme için ReadOnlySpan<T> döndürüyor
  • Temel dal kodu, source.GetType() == typeof(TSource[]) veya source.GetType() == typeof(List<TSource>) kontrolünden sonra span elde ediyor
    • Diziler Unsafe.As<TSource[]>(source) ile işleniyor
    • Listeler, iç diziden span almak için CollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source)) kullanıyor
  • Kodda source.GetType() iki kez çağrılıyor ve cast sonrası null kontrolüyle ilerlenmiyor; ancak bu yaklaşım, .NET performans uzmanlarının C# derleyicisi ve JIT optimizasyonlarını dikkate alarak yaptığı bir tercih
  • Yüksek düzeyde optimize edilmiş .NET stack’inde mikro optimizasyonlar, dışarıdan göründüğünden farklı sonuçlar verebiliyor

CollectionsMarshal.AsSpan() kısıtları

  • List<TSource>, iç yapısında bir diziye referans tutuyor; liste kapasitesinin artması veya azalması gerektiğinde yeni bir dizi oluşturup ona referans veriyor
  • CollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source)), bu iç dizi üzerinden Span<TSource> elde ediyor
  • Liste kapasitesi herhangi bir şekilde değişirse, bu yöntemle elde edilen dizi geçersiz hale gelebilir
  • Bu kısıt nedeniyle yield gibi gecikmeli dolaşım içeren bazı Enumerable işlemlerinin bu optimizasyona dayanması zor
  • System.Runtime.CompilerServices.Unsafe adının kendisi bile bu riski gösteriyor

TryGetSpan() çağrı kapsamı

  • NDepend ile System.Linq.dll taranarak TryGetSpan() için doğrudan ve dolaylı çağıranlar inceleniyor
  • Analiz edilen assembly yolu C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.0-rc.1.24431.7\System.Linq.dll
  • TryGetSpan() üzerinden kod sorgusu oluşturulup çağıranlar belirleniyor, ardından eşleşen 56 metod bağımlılık grafiği olarak dışa aktarılıyor
  • Çok sayıda standart Enumerable metodu, koleksiyon dizi veya listeyse span ile dolaşmayı deniyor
  • Ancak listenin iç dizisini doğrudan kullanma yöntemi güvenli olmadığından, gecikmeli yürütme gerektiren işlemlerde sınırlamalar sürüyor

Özel iterator tabanlı optimizasyonlar

  • İkinci benchmark, Consolidate LINQ’s internal IIListProvider/IPartition into base Iterator class PR’ındaki örnekleri kullanıyor
  • Test edilenler arasında Distinct().First(), Append().Select().Last(), Reverse().Count(), DefaultIfEmpty().Select().ElementAt(), Skip().Take().ElementAt(), Union().First(), Select().Where().Select().Sum() yer alıyor
  • .NET 9’da bazı çağrı zincirleri son derece hızlanıyor
    • DistinctFirst: 65.318 ns11.192 ns, 328 B atama → atama yok
    • AppendSelectLast: 4,122.007 ns2.661 ns, 144 B atama → atama yok
    • DefaultIfEmptySelectElementAt: 4,090.818 ns5.724 ns, 144 B atama → atama yok
    • RangeUnionFirst: 66.309 ns6.193 ns, 344 B atama → atama yok
    • ListSkipTakeElementAt: 6.268 ns2.916 ns
    • RangeReverseCount: 11.024 ns6.134 ns
  • Buna karşılık SelectWhereSelectSum, .NET 8’deki 3,959.622 ns değerinden .NET 9’da 4,460.008 ns’ye yavaşlıyor ve 112 B atama da korunuyor

Yaygın LINQ zincirlerini tanıma

  • .NET performans ekibi, yaygın LINQ çağrı zincirlerini tanıyacak şekilde kod tasarladı
  • Belirli bir zincir algılandığında, iş akışını daha verimli işleyen özel bir iterator oluşturuluyor
  • Zincir Count(), First(), Last(), ElementAt(), Sum() gibi metotlarla bittiğinde ek optimizasyon mümkün oluyor
  • Örneğin OrderBy(criteria).First(), Min(criteria) benzeri şekilde çalışacak biçimde optimize edilebiliyor

Iterator<T> ve türetilmiş sınıf yapısı

  • LINQ içinde soyut temel sınıf Iterator<T> ve ondan türeyen 40 sınıf bulunuyor
  • Bu sınıfların tamamı Enumerable sınıfı içine iç içe tanımlanmış durumda
  • Iterator<T> soyut bir sınıf olsa da metotları virtual; bu yüzden türetilmiş sınıflar yalnızca ihtiyaç duydukları metotları override ediyor
  • Bu yapı, çağrı zincirine özel davranışların temelini oluşturuyor

ListWhereSelectIterator<TSource, TResult> örneği

  • ListWhereSelectIterator<TSource, TResult>, listelerdeki Where(...).Select(...) zincirini tek bir iterator ile işliyor
  • Bu iterator, ListWhereIterator<TSource, TResult> içindeki Select() override tarafından oluşturuluyor
  • ListWhereIterator<TSource>, Enumerable.Where() kaynak nesnenin List<TSource> olup olmadığını kontrol ettiğinde oluşturuluyor
  • ListWhereSelectIterator<TSource, TResult>, TryGetFirst() veya TryGetLast() gibi metotları override etmiyor
  • Performans iyileştirmesinin özü, listelerde çok yaygın olan Where(...).Select(...) zincirinin iki ayrı iterator yerine tek bir iterator içinde birleştirilmesi
    • MoveNext() içinde _predicate ve _selector adlı iki delegate birlikte çağrılıyor

IListSkipTakeIterator<TSource> örneği

  • IListSkipTakeIterator<TSource>, uygun olduğunda oluşturulan özel bir iterator
  • MoveNext(), _state - 1 değerini listenin 0 tabanlı indeksi olarak kullanıyor
  • Ayrı bir indeks alanı kullanmak daha okunabilir olurdu; ancak iterator alan boyutunu küçültmek için _state içine bias eklenerek saklanıyor
  • Bu iterator’daki optimizasyonun özü, _minIndexInclusive ile _maxIndexInclusive aralığı dışındaki öğelerin gereksiz yere dolaşılmaması

Sadece migration ile gelen ek optimizasyonlar

  • .NET 9’da birçok genel LINQ senaryosu daha hızlı çalışıyor
  • Yeni .NET sürümündeki iyileştirmeleri almak için gerekenler migration ve yeniden derleme
  • LINQ başka şekillerde de optimize ediliyor
    • Tamsayı sequence’lerini toplama gibi durumlarda, mümkün olan yerlerde SIMD kullanılıyor
    • Boş sequence’ler erken algılanarak enumeration maliyeti düşürülüyor
  • DeepDotnet videos, Scott Hanselman ve Stephen Toub’un yer aldığı .NET öğrenme materyalleri olarak izlenebilir

1 yorum

 
GN⁺ 2024-10-20
Hacker News yorumları
  • LINQ’nun en faydalı kısmının IQueryable’ın sözdizimi ağacı tabanlı genişletme yapısı ya da dile gömülü söz dizimi değil, IEnumerable genişletme metotları olduğunu düşünüyorum
    Eskiden biraz kafa karıştırıcı biçimde “LINQ to Objects” diye adlandırılıyordu ve C#’ı fonksiyonel tarzda daha özlü yazmayı sağlıyor
    Asıl yazı da ağırlıklı olarak bu genişletme metotlarının optimizasyonlarını ele alıyor
    Haskell öğrendikten sonra bu yaklaşım bana ancak oturdu; Haskell’in gecikmeli değerlendirme gibi bazı avantajlarını ve tuzaklarını da kısmen paylaşıyor
    Rastgele kullanılırsa anlaşılması zor ve yavaş koda dönüşebilir; bu yüzden ekipte temel fonksiyonel deyimlere ve gecikmeli değerlendirmeye aşina biri yoksa önermek istemem

    • Ben de IEnumerable ve IQueryable üzerindeki LINQ genişletmelerinin fonksiyonel yönünü tercih ediyorum
      Akıl yürütmesi daha kolay; Entity Framework gibi yerlerde her zaman en hızlı seçenek olmasa da genelde oldukça iyi bir tercih
      EF yerine Dapper kullanmayı da seviyorum
      Yine de C# projeleri saçma derecede fazla soyutlama katmanına sahip olma eğiliminde ve “enterprise” geliştirme çoğunlukla bakması acı verici
    • Ben de LINQ’yu bu şekilde kullanıyorum
      İsimlendirmede biraz standart dışı kısımlar var ama gereken her şey mevcut
      Eric Lippert, LINQ ile bağlantılı olarak monadları açıklayan harika bir yazı dizisi yazmıştı: https://ericlippert.com/2013/04/02/monads-part-twelve/
    • LINQ’da her zaman yalnızca metot söz dizimini kullandım
      Ana dilin içinde başka bir “gömülü” dil bulunmasını sevmiyorum; nihai sonucun da eninde sonunda C#’a dönmesi gerekiyor
      Entity Framework ya da Dapper gibi ORM’ler kullanmadığımda bile, SQL dahil veri erişim mantığını ayrı, soyutlanmış bir projede tutmayı tercih ederim
      Böylece tüm uygulamaya yayılmaz ve başka bir RDBMS gerektiğinde değiştirilebilir
      20 yılda gerçekte böyle bir şey yalnızca bir kez oldu gerçi
      Junior geliştiriciler LINQ kullanırken yanlarına profiler ve debugger vermek, içeride neler olduğunu anlamalarına yardımcı oluyor
      Bazen önce for döngüsü ve normal C# mantığıyla yazdırıp sonra LINQ uygulamasıyla karşılaştırmak, iki yaklaşımın artılarını ve eksilerini görmeleri açısından da faydalı
    • Haskell’i seviyorsanız LINQ’nun sorgu söz dizimini kullanarak kombinatör parser oluşturma gibi başka kullanımlar da hoşunuza gidebilir
      Sorgu söz dizimi IEnumerable’a özel olarak sabit kodlanmış değil; yalnızca varsayılan davranış bu ve neredeyse her yerde kullanılabilir
      Operatör aşırı yüklemeye biraz benzer şekilde çalışır
      [1]: https://github.com/acple/ParsecSharp/blob/da8d0cb9ec39e28dd9...
    • LINQ söz dizimi yarın ortadan kalksa çok da üzülmem ama fonksiyonel bileşim gerçekten güçlü ve bakımı da daha kolay
  • dotnet ekibinin araçlara neden daha fazla kaynak ve zaman yatırmadığını anlamıyorum
    doctest ve dokümantasyon üretimi, gerçek kodun yanında daha iyi ve daha hızlı birim testleri yazma imkânı, kaynak koda erişilebilirlik, F12’ye basınca DLL’i decompile etmek zorunda kalmayan bir ortam, pkg.go.dev ya da docs.rs gibi paketler ve dokümantasyon için merkezi bir hub gerekiyor
    NuGet paketlerinin çoğunda hiç dokümantasyon yok ya da yalnızca GitHub README’si veya kısa bir wiki var
    Rust, Go, Java, Python gibi diğer ekosistemler bu konuda ışık yılı ileride

    • Microsoft’un OpenAI’a yatırım yapmasının, .NET/NuGet paket dokümantasyonunda gezinmenin tek mantıklı yolu olduğu için olduğu şakasını yapar oldum
      Ama korkunç olan, bunun gerçeğe de yakın olduğunu düşünmem
    • Artık Microsoft dokümanlarında, baktığınız metodun kaynak koduna doğrudan giden bağlantılar var
      Örnek: https://learn.microsoft.com/en-us/dotnet/api/system.string.s...
      NuGet paketlerinin kaynak kodu için de Source Link açılırsa bu kolayca mümkün; ancak hâlâ nispeten yeni bir özellik olduğu için tüm paketler uygulamış değil
    • Katılıyorum ama açık kaynak C#’ın görece yeni bir şey olması da muhtemelen nedenlerden biri
      C# kodunun büyük kısmı hâlâ şirketlerde kapalı kaynak olarak yazılıyor gibi
      Microsoft son yıllardaki gibi açık yöne doğru gitmeyi sürdürürse zamanla iyileşeceğini düşünüyorum
      Bu özelliklerin bazılarını Resharper gibi araçlar sağlıyor; aralarında birbirlerinin alanına girmeme yönünde açık ya da örtük bir mutabakat mı var diye merak ediyorum
      Dürüst olmak gerekirse C# projelerinde gördüğüm dokümantasyonun çoğu düşük kaliteliydi, sonunda kaynak koda bakıyordum
      Otomatik tamamlama araçları çok olsa da okuma açısından pek yardımcı olmadığını, daha çok yazmaya yardımcı olduğunu deneyimledim
    • Sandcastle Help File Builder çok eskiden beri var ve hatırladığım kadarıyla Microsoft içi bir proje olarak başlamıştı; ama garip şekilde onu kullanan kütüphane sayısı az
      https://github.com/EWSoftware/SHFB
    • Testleri kodun yanına yazmanın bir yolu da var: https://clipperhouse.com/go-test-csharp/
      Tavsiye eder miyim pek emin değilim
      Kendim denedim sonra geri aldım; derleme çıktısı önbelleklemesi kötüleştiğinden olsa gerek testler daha uzun sürüyor gibiydi
  • “LINQ performans iyileştirmeleri” demektense “kendi List implementasyonlarının performans iyileştirmeleri” demek daha doğru olur
    Microsoft, genel iyileştirmelerden çok kendi ihtiyaç duyduğu kısımları iyileştirmeye zaman harcıyor gibi görünüyor
    LINQ’ya, özellikle de metot genişletmelerine değil sorgu söz dizimine yatırım gerekiyor
    Esas olarak lambda tahsislerinin ve mümkünse derleme zamanında lambda küçültmenin ele alınması gerekiyor
    Değer türü yerel lambdalar ya da lambda tahsislerinin bugünkü gibi ek yük oluşturmamasını sağlayacak bir strateji gerekiyor
    LINQ değişkenlerinde de artık joker karakter (_) desteği olmalı; lambdalara eklenirken tamamen göz ardı edilmişti
    Ayrıca LINQ ifadelerinin son öğesi olarak select ... yerine IEnumerable, Option gibi yükseltilmiş türler kullanılabilmeli
    Bazı kullanım senaryolarında select gereksiz ek yük yaratıyor ve kuyruk özyinelemeli LINQ ifadeleri gibi şeyleri de kısıtlıyor
    Benim kütüphanem gibi LINQ’ya tümüyle yaslanan ama IEnumerable, IQueryable ya da LINQ genişletmelerini kullanmayan kütüphaneler sürekli görmezden geliniyor
    Bunun nedeni Microsoft’un yalnızca kendi projelerinin performans iyileştirmelerine odaklanması
    Bunun iyi bir örneği iyileştirilmiş lambda çıkarımı
    ASP.NET Core’un Minimal API’leri için gerektiği için öne çekildi
    Dil ve framework özelliklerinin önemli bir kısmı, topluluğun ihtiyaçlarından çok Microsoft’un iç ihtiyaçlarıyla şekilleniyor gibi
    En kötüsü de LINQ genişletmeleri olan Select, SelectMany, Where dışında GetAwaiter gibi sihirli metotlar kümesinin sürekli büyümesi
    Microsoft, bu sihri ortadan kaldırmak için gerçekten gerekli olan yüksek mertebeli kind özelliklerini eklemek yerine, kendileri, çoğunlukla da derleyici için özellik ekliyor
    Bu yüzden her şey zayıf tipli kalıyor ve derleyici bunları ancak kabaca keşfedebiliyor
    LINQ, diller arasındaki temel farklılaştırıcılardan biri; ama C# 3’ten beri neredeyse kendi hâline bırakıldı
    LINQ’yu hâlâ yalnızca liste dolaşımı, özellikle de kendi liste implementasyonlarını dolaşmak için yararlı görmek gerçekten üzücü
    Performans iyileştirmelerinin kendisi için teşekkür ederim ve birçok kullanıcıya yardımcı olacaktır; fakat odak sürekli dar tutulduğu için potansiyeli sınırlıyor
    [1] https://github.com/louthy/language-ext/

    • Yararlı geri bildiriminiz varsa dotnet/runtime üzerinde issue açmak ya da PR göndermek iyi olur
      Yazıda ele alınan LINQ performans iyileştirmelerinin önemli bir kısmı bu şekilde geldi
    • Kütüphane çok ilginç görünüyor, ama baştan kolayca görmezden gelinebilecek bir biçimde konumlandırılmış gibi de duruyor
      Çok sayıda using ifadesi var; projeleri gerekli birimlere bölmeyi ve sorumlulukları ayırmayı anlayan biri için bu büyük sorun değil
      Ancak çoğu geliştirici projeleri böyle yapılandırmıyor ve bu tür küçük ayrıntılar ortalama bir geliştirici için engel olabilir
      Junior geliştiriciler standart LINQ söz dizimi ve metotlarında, özellikle de performans konusunda zaten sıkça zorlanıyor
      README’de bundan bahsedilmiş olması iyi
      Genelde insanlar kütüphaneyi “satmakla” meşgul olur; nerede güçlü olduğunu ve amacının ne olduğunu gerçekten yazmış olmanız hoşuma gitti
      İdyomatik olmadığına dair not da C#/.NET öğrenen biri için sorun olabilir
      Microsoft muhtemelen araçların ve dilin belirli pratikleri izlemesini ister; fonksiyonel programlamaya doğal biçimde uydurulmuş adlandırmalar, Microsoft’un iyileştirmeleri değerlendirirken oldukça büyük bir engel olarak görebileceği bir şey olabilir
      Depoya yıldız verdim ve yaptığınız işle çok ilgileniyorum
      Son dönemde yaptığım birkaç büyük uygulamada, Optiona kabaca benzer görünen bir Result tipi kullandım
      Ama kütüphaneye tekrar bakınca, fonksiyonel programlamayı epey bildiğimi sanmama rağmen aslında öyle olmadığını hissettim
      C#’ta oldukça iyiyim ve karmaşık işler de yaptım; fakat fonksiyonel programlama, çok okusam da hâlâ öğrenmesi zor geliyor ve F# For Fun And Profit şimdiye kadar en anlaşılır olanıydı
      Sonuçta bu, sizin bir şeyi yanlış yaptığınız anlamına gelmiyor
      Microsoft, kendi ekosisteminin büyük çoğunluğunu oluşturan ortalama veya başlangıç seviyesindeki geliştiricileri hedefleyecektir
      Bu kütüphanenin içeriden iyileştirmeler elde edebilmesini isterim
      Muazzam zaman harcandığı belli ve GitHub yıldız sayısı bile gerçekten kullanan ve fayda gören insanlar olduğuna dair yeterli bir işaret
      Sözlerim küçümseyici geldiyse özür dilerim; yaptığınız iş ilginç ve bilmediğim kavramları yavaş yavaş öğrenebilecek kadar dokümantasyon da var gibi görünüyor
  • C# F#’tan ne kadar çok şey ödünç alırsa o kadar iyi
    Ayrık birleşimlerin sonunda C#’a gelmesini ve alan modellemesini düzgün yapabilmeyi bekliyorum

    • .NET topluluğunda bu tür konuların sık sık gündeme gelmesi ilginç
      Her seferinde aklıma “neden sadece F# kullanmıyoruz?” sorusu geliyor
      C# uzun yıllardır arayı kapatmaya çalışıyor
      .NET ekosistemindeki birçok yeniliği F# itiyorsa ve özellik açısından yıllarca öndeyse, neden bu çabayı kullanarak ödüllendirmediklerini merak ediyorum
      İstediğiniz bir dil geliştirme yönü varsa bunu gerçek tercihlerle teşvik etmelisiniz
      Java ekosisteminde de bu şekilde etkili oldu ve artık Java da iyileşiyor
      Pazar büyüdükçe mühendislik çabası da artan pozitif bir geri bildirim döngüsü oluşabilir
      Yıllarca forumları okudukça C# tarafının “sadece” kendi tarafında kalıp beklemek istediği hissi güçleniyor
      Biraz kabilecilik gibi görünüyor; takımın “C#” olduğu hissi var
      Diğer dil ekosistemlerinde bu kültürü pek görmedim ve F# .NET dışında başka bir ekosistemde olsaydı belki de çoktan gelişip serpilmiş olurdu izlenimine kapılıyorum
    • Ölçü birimi tiplerini de gerçekten istiyorum
      Mühendislik veya bilim kodunun bakımını çok daha kolaylaştırıyor
    • OneOf[0] ve Dunet[1] kullanarak ayrık birleşimleri zaten oldukça kolay biçimde dahil edebilirsiniz
      Gerçek kullanım örneği: https://chrlschn.dev/blog/2024/07/csharp-discriminated-union...
      [0] https://github.com/mcintyre321/OneOf
      [1] https://github.com/domn1995/dunet
    • F#’ın C# ve VB.NET özellikleri için bir deneme alanı olduğu herkesin bildiği bir sır ve Hanselman gibi resmî isimler de bunu birçok kez dile getirdi
  • Başka dillerde veya ekosistemlerde çalışırken en çok özlediğim şey LINQ oluyor
    Standart kütüphanede böyle bir özelliğin olması gerçekten harika; verilen kısıtlar içinde de güzel tasarlanmış

  • .NET 9’daki tüm performans iyileştirmelerini ele alan, yıllık kitap hacmindeki yazıda ilgili bir bölüm var
    https://devblogs.microsoft.com/dotnet/performance-improvemen...
    Garip bir şekilde HN yeniden göndermeye izin vermediği için yazı ön sayfaya çıkamadan gömüldü

  • LINQ’e alışınca ve genelde LINQ’in parladığı alanlarda çalışmaya başlayınca başka bir yönteme geri dönmek istemiyorsunuz

    • Dostlar, LINQ bağımlısı olmayın
      LINQ sizi yakalayacak ve LINQ olmayan ortamları suçlar hale geleceksiniz
    • Yine de polars gibi şeylerden daha az güçlü
  • dotnet ile uçtan uca web geliştirme öğrenmek için iyi, kapsamlı bir kitap veya eğitim olup olmadığını merak ediyorum
    Bulduklarımın çoğu ya fazla temel düzeydeydi ya eskiydi ya da düşük kaliteliydi

    • .NET web geliştirmede bugünlerde yeni ve gözde olan şey Blazor, ama Microsoft blog çevresi dışında pek popüler değil ve olacak gibi de görünmüyor
      Kişisel olarak Silverlight ile aynı yolu izleyeceğini düşünüyorum
      Eski teknolojiler de .NET 9’da hâlâ var, çalışıyor ve bakımı yapılıyor
      Bugünlerde .NET ile web geliştirme çoğunlukla HTTP/JSON/REST API oluşturup bunu istediğiniz frontend framework’üne bağlamak anlamına geliyor
      Ben React veya NextJS kullanıyorum
      Arama terimi olarak ASP.NET WebApi ya da daha modern biçimiyle ASP.NET Minimal API iyi olur
      Razor kullanan .NET MVC sunucu tarafı render etme hâlâ mümkün
      ASP.NET MVC’nin işaretleme dili olduğu için “ASP.NET MVC Razor” diye arayabilirsiniz
    • Son zamanlarda C# ile web geliştirmeye ilgi duymaya başladım
      İyi ya da kötü, .NET’te web uygulaması geliştirmenin fiilî doğru yolu ASP.NET gibi görünüyor
      Alternatiflerin azlığı biraz şüpheli olsa da
      “ASP.NET Core in Action”ın yazarı Andrew Lock’un katıldığı bir podcast dinledim; konuya hâkim biri gibi görünüyordu
      Kitabı henüz okumadım ama aradığınız kitap olabilir
      1: https://dotnetcore.show/season-6/navigating-the-aspnet-core-...
      2: https://www.manning.com/books/asp-net-core-in-action-third-e...
    • Biraz ana akım dışı ama F# ve Fable kombinasyonu çok güçlü
      Sunucuda ASP.NET üzerinde Giraffe çalıştırabilirsiniz; C#’a benzer performans veren işlevsel programlama katmanı
      Frontend’de gerçek bir işlevsel programlama diliyle React yazabilirsiniz
      Elbette frontend ile backend arasında F# kodu da paylaşabilirsiniz
    • Ben deneyerek öğrendim, ama referans olabilecek birkaç kaynak yazayım
      Kitap olarak Mark J Price’ın “C# 12 and .NET 8 - Modern Cross-Platform Development Fundamentals - Eighth Edition: Start building websites and services with ASP.NET Core 8, Blazor, and EF Core 8” ve Xiaodi Yan’ın “Web API Development with ASP.NET Core 8: Learn techniques, patterns, and tools for building high-performance, robust, and scalable web APIs” kitapları var
      Eğitim olarak YouTube’da IAmTimCorey ve Shawn Wildermuth serileri var
    • Sunucuda render edilen UI ise Razor kullanan kaynakları arayın; başlangıçta Blazor kaynaklarından kaçınmak iyi olur
      .NET backend ile JS frontend kombinasyonu için Minimal API kullanan kaynakları aramanız yeterli
      MVC de iyi, ama çok fazla geriye dönük uyumluluk yükü olduğu için Minimal API ortaya çıktı
  • Bu yorum eriştesi yığınından daha iyi bir yol olmalı
    Modern .NET kodu gördüğüm her seferinde gözlerim ağrıyor

    • O attribute’lar yazıda kullanılan benchmark kütüphanesine karşılık geliyor
      Unit test ve benchmark kodu genelde bir ölçüde spagetti gibi görünür
      Yine de gerçek iş mantığında böyle bir PR’ı ben onaylamazdım
      Gerçekten sevmiyorsanız AspNetCore gibi şeyleri de tek bir attribute’a dokunmadan kullanabilirsiniz
    • Hangi .NET koduna baktığınızı bilmiyorum
      Ben attribute’ları neredeyse hiç kullanmıyorum
  • Zincir Count(), First(), Last(), ElementAt(), Sum() gibi metotlarla bittiğinde daha fazla optimizasyon yapılabileceği; örneğin OrderBy(criteria).First() ifadesinin Min(criteria) gibi çalışacak şekilde optimize edilebileceği kısmı faydalı olabilir
    Ama en başta daha iyi kod yazmak gerekir
    Dinamik olarak oluşturulan zincirler için ilginç olabilir, ama elde yazılmış kodda böyle işlemler yapılıyorsa bu biraz çarpık bir olumlu pekiştirme gibi geliyor
    Kütüphane verimsiz kalıpları tanıyıp düzeltiyor gibi
    En azından temel kodun iyileştirilmesini öneren bir geri bildirim olmasını isterim