Hızlı ve Öfkeli : .Net Core 3.1 Upgrade

Semih Şenvardar
hesapkurdu-development
4 min readMar 16, 2020
source : collider.com

Merhabalar,

Microsoft’un .Net Core 2.2 versiyonu için yaptığı End Of Life duyurusu ile beraber Hesapkurdu.com’daki dockerize olmuş .Net Core uygulamalarımızı LTS (Long Term Support) desteği olan 3.1 versiyonuna yükseltme kararı aldık. Açıkçası çok sorun yaşayacağımızı düşünmüyorduk. Bize kalırsa birkaç konfigürasyon değişikliği ve package temizliği ile 3.1'e ‘Merhaba’ diyecek, tüm dertlerimize son verecektik. İşler pek de sandığımız kadar kolay çözümlenmedi. Ben de bu yolda tecrübe ettiğimiz zorlukları ve bulabildiğimiz çözümleri sizinle paylaşmak istedim.

Peki Neler Oldu ?

İlk olarak gözümüze çarpan değişiklik 2.2'de kullandığımız pek çok package’ın ( Microsoft.AspNetCore, etc.) obsolete hale gelmesi. Paket temizliği hem solution içerisindeki uygulamaların nuget restore sürelerini hissedilir derecede azalttı hem de disk space ve package management konularında epey elimizi rahatlattı.

DI tool olarak kullandığımız Simple Injector yerine built-in Microsoft.Extensions.DependencyInjection kütüphanesini kullanınca daha light-weight bir Startup sürecine kavuşmuş olduk.

Azalan build süreleri, paket temizliği, clean infrastructure ile madalyonun hızlı tarafına şahit olmuştuk. Sıra geldi madalyonun öteki yüzüne.

Query Optimizasyonları

Unit Test’lerimizi çalıştırdığımızda beklemediğimiz kadar testi failed halde bulduk. Frekansı en yüksek olan hata da EF Core 3.0 ile gelen ‘ LINQ queries are no longer evaluated on the client’ değişikliğinden kaynaklı olan ‘Client side GroupBy is not supported’ hatasıydı.

  • Eğer query’nin ürettiği dinamik nesne için GroupBy() kullanacaksanız, öncelikle sizden nesneyi cast etmeniz isteniyor ( AsEnumerable(),ToList(),ToDictionary() etc.). Compile anında farkedilemeyen bu durum runtime’da can sıkıcı sonuçlara sebep olabildiğinden testable code yazmakta fayda var.

Örnek vermek gerekirse, aşağıdaki ilk sorgu bahsi geçen hatayı vereceği için IGrouping<> özelliğini doğru kullanabilmek adına dinamik nesneyi AsEnumerable() olarak cast ediyoruz.

Kestrel/IIS Problemleri

AllowSynchronousIO özelliği , sunuculardaki senkron çalışan IO api’leri (HttpReqeuest.Body.Read, HttpResponse.Body.Write, Stream.Flush, vb.) aktif/pasif etmemize olanak sağlayan bir özellik ve .Net Core 3.1'de default olarak disabled olarak geliyor. Geçici bir çözüm olarak aşağıdaki gibi Startup dosyasında kısa bir konfigürasyon satırı ile bu sorunu aşabilirsiniz.

Ya da Program.cs içerisini aşağıdaki gibi düzenleyebilirsiniz.

Best practice olmasından ziyade yolunuza devam etmenizi sağlayan bir çözüm olduğunu belirtmeden geçmeyelim. İdeal olan action’larımızı asenkron hale getirip .Net Core 3.1'in nimetlerinden maksimum faydalanabilmek.

Ek olarak appsettings.json üzerinden Kestrel konfigürasyonlarınızı da yapmanızı ihmal etmeyin. Bizim gibi container’larınız AWS cluster’ları üzerinde ise communication problemleri (Network is unreachable) yaşamanız çok büyük ihtimal dahilinde.appsettings.json’daki Kestrel section’ını aşağıdaki gibi düzenlersek sürprizleri minimize etmiş oluruz.

Docker Konfigürasyonları

Sql bağlantılarında TLS 1.2 protokolü kullanıyorsanız mevcuttaki dockerize olmuş uygulamalarınızın Dockerfile’larını yeniden ayarlamanız gerekiyor, nitekim biz de .Net Core 3.1 geçişi ile tekrar düzenleme imkanı bulduk.

İlk olarak .Net Core 3.1 için hangi docker image’ını kullanacağınıza karar vermeniz gerekli. Debian tabanlı buster veya ubuntu tabanlı bionic’i tavsiye ederim. (Not: TLS 1.2 desteği bionic image’ında mevcutta hazır olarak gelirken buster’da Dockerfile üzerinden konfigüre etmeniz bekliyor.)

Aşağıdaki linkten seçebileceğiniz runtime image’larını siz de görebilirsiniz.

Biz light-weight olması nedeniyle buster sürümünü tercih ettik. Tabii bu tercih nedeniyle TLS 1.2 konfigürasyonlarını da Dockerfile’da düzenlememiz gerekti. Çözüm olarak aşağıdaki gibi birkaç konfigürasyon satırı eklememiz işimizi gördü.

Genel Konfigürasyon Değişiklikleri

  • Logging için kullandığımız Serilog’u .Net Core 2.2 ‘de CreateDefaultBuilder içerisinde belirtebiliyorken, 3.1’de bu tanımları ConfigureWebHostDefaults section’ı içerisinde yapmanız isteniyor.

Not : Eğer ApplicationInsights kullanıyorsanız onun da konfigürasyonu Program.cs ‘den Startup.cs içerisine taşınmış bulunmakta.

  • AddMvc() metodunu AddControllers() ile değiştirmiş olmanız gerekiyor.(WebAPI özelinde)
  • UseMvc() metodu yerine UseRouting() ve UseEndpoints() metodlarını kullanmanız gerekli.(Not: Eğer Authentication veya Authorization kullanıyorsanız UseRouting ve UseEndpoint’i nested bir şekilde kullanmak zorunda değilsiniz. Bu tür durumlarda UseRouting, UseAuthentication veya UseAuthorization metodlarından önce set edilmiş olmalı. UseEndpoints metodu ise en son set edilmiş olmalıdır.
  • IHostingEnvironment yerine IHostEnvironment kullanmalısınız.
  • NewtonSoft.Json ,default olarak System.Text.Json ile yer değiştirdi. Eğer hala NewtonSoft.Json kullanmanız gerekiyorsa aşağıdaki gibi kullanmaya devam edebilirsiniz. (Not: Performans olarak System.Text.Json çok daha iyi sonuç veriyor.)

Özetlemek Gerekirse

.Net Core 3.1 geçişi için beklediğimizden daha fazla efor sarfetmek durumunda kaldık ama sonuçları da aynı derecede güzel oldu bizim için. Linq query’lerinde yazdığımız static inner function’ların memory leak’e sebebiyet verebilme ihtimalinden dolayı exception fırlatan, 2.2'ye göre daha güvenli, performans odaklı ve memory management konusunda daha yetenekli olan 3.1'e geçmenizi tavsiye ederiz.

Sizler de bu tarz geçişlerde yaşadığınız sorunlarınızı paylaşmak ve çözüm bulabilmek adına bizimle dev@hesapkurdu.com adresinden iletişime geçebilirsiniz.

Yeniden görüşmek üzere.

Saygılarımla.

--

--

Semih Şenvardar
hesapkurdu-development

just some stuff about software development, sharing is caring i guess.