.NET 9.0 LINQ performans iyileştirmeleri
(blog.ndepend.com)-.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şırkenTryGetSpan()ileReadOnlySpan<T>elde edip yineleme maliyetini düşürmek, temel iyileştirmelerden biri TryGetSpan(),TSource[]veList<TSource>türlerini tür karşılaştırmasıyla ayırt ediyor; ancakList<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 veCount(),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()sonucunuIEnumerable<int>olarak tutup ardındanCount,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.0hedeflemeli 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 ns → 3,043.563 ns, 32 B atama → atama yokLinqAny: 17,096.735 ns → 2,483.927 ns, 32 B atama → atama yokLinqFirst: 15,289.747 ns → 2,243.341 ns, 32 B atama → atama yokLinqSingle: 21,684.114 ns → 4,884.329 ns, 32 B atama → atama yokLinqAll: 10.588 ns → 2.562 ns, 32 B atama → atama yokLinqLast: 15.967 ns → 6.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çinReadOnlySpan<T>döndürüyor - Temel dal kodu,
source.GetType() == typeof(TSource[])veyasource.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
- Diziler
- 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 veriyorCollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source)), bu iç dizi üzerindenSpan<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
yieldgibi gecikmeli dolaşım içeren bazıEnumerableişlemlerinin bu optimizasyona dayanması zor System.Runtime.CompilerServices.Unsafeadının kendisi bile bu riski gösteriyor
TryGetSpan() çağrı kapsamı
- NDepend ile
System.Linq.dlltaranarakTryGetSpan()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
Enumerablemetodu, 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 ns → 11.192 ns, 328 B atama → atama yokAppendSelectLast: 4,122.007 ns → 2.661 ns, 144 B atama → atama yokDefaultIfEmptySelectElementAt: 4,090.818 ns → 5.724 ns, 144 B atama → atama yokRangeUnionFirst: 66.309 ns → 6.193 ns, 344 B atama → atama yokListSkipTakeElementAt: 6.268 ns → 2.916 nsRangeReverseCount: 11.024 ns → 6.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ı
Enumerablesı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>, listelerdekiWhere(...).Select(...)zincirini tek bir iterator ile işliyor- Bu iterator,
ListWhereIterator<TSource, TResult>içindekiSelect()override tarafından oluşturuluyor ListWhereIterator<TSource>,Enumerable.Where()kaynak nesneninList<TSource>olup olmadığını kontrol ettiğinde oluşturuluyorListWhereSelectIterator<TSource, TResult>,TryGetFirst()veyaTryGetLast()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ştirilmesiMoveNext()içinde_predicateve_selectoradlı iki delegate birlikte çağrılıyor
IListSkipTakeIterator<TSource> örneği
IListSkipTakeIterator<TSource>, uygun olduğunda oluşturulan özel bir iteratorMoveNext(),_state - 1değerini listenin 0 tabanlı indeksi olarak kullanıyor- Ayrı bir indeks alanı kullanmak daha okunabilir olurdu; ancak iterator alan boyutunu küçültmek için
_stateiçine bias eklenerek saklanıyor - Bu iterator’daki optimizasyonun özü,
_minIndexInclusiveile_maxIndexInclusivearalığı 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
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,IEnumerablegenişletme metotları olduğunu düşünüyorumEskiden 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
IEnumerableveIQueryableüzerindeki LINQ genişletmelerinin fonksiyonel yönünü tercih ediyorumAkı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
İ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/
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
fordö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ıSorgu söz dizimi
IEnumerable’a özel olarak sabit kodlanmış değil; yalnızca varsayılan davranış bu ve neredeyse her yerde kullanılabilirOperatör aşırı yüklemeye biraz benzer şekilde çalışır
[1]: https://github.com/acple/ParsecSharp/blob/da8d0cb9ec39e28dd9...
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.devya dadocs.rsgibi paketler ve dokümantasyon için merkezi bir hub gerekiyorNuGet 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
Ama korkunç olan, bunun gerçeğe de yakın olduğunu düşünmem
Ö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
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
https://github.com/EWSoftware/SHFB
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
Listimplementasyonlarının performans iyileştirmeleri” demek daha doğru olurMicrosoft, 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ştiAyrıca LINQ ifadelerinin son öğesi olarak
select ...yerineIEnumerable,Optiongibi yükseltilmiş türler kullanılabilmeliBazı kullanım senaryolarında
selectgereksiz ek yük yaratıyor ve kuyruk özyinelemeli LINQ ifadeleri gibi şeyleri de kısıtlıyorBenim kütüphanem gibi LINQ’ya tümüyle yaslanan ama
IEnumerable,IQueryableya da LINQ genişletmelerini kullanmayan kütüphaneler sürekli görmezden geliniyorBunun 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,WheredışındaGetAwaitergibi sihirli metotlar kümesinin sürekli büyümesiMicrosoft, 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/
dotnet/runtimeüzerinde issue açmak ya da PR göndermek iyi olurYazıda ele alınan LINQ performans iyileştirmelerinin önemli bir kısmı bu şekilde geldi
Çok sayıda
usingifadesi var; projeleri gerekli birimlere bölmeyi ve sorumlulukları ayırmayı anlayan biri için bu büyük sorun değilAncak ç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 birResulttipi kullandımAma 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
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
Mühendislik veya bilim kodunun bakımını çok daha kolaylaştırıyor
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
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
LINQ sizi yakalayacak ve LINQ olmayan ortamları suçlar hale geleceksiniz
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
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
İ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...
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
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
.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
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
Ben attribute’ları neredeyse hiç kullanmıyorum
Zincir
Count(),First(),Last(),ElementAt(),Sum()gibi metotlarla bittiğinde daha fazla optimizasyon yapılabileceği; örneğinOrderBy(criteria).First()ifadesininMin(criteria)gibi çalışacak şekilde optimize edilebileceği kısmı faydalı olabilirAma 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