.NET Framework’ten .NET Core‘a Göç Hikayemiz

Merhabalar,

Bu yazıda .NET ekosisteminin yeni framework’ü .NET Core’dan, birlikte gelen bazı yeniliklerden ve firmamızda projelerde kullanılan .NET Framework’ü ile yazılmış kütüphanenin .NET Core’a geçiş sürecininden bahsedeceğim.

Göç hikayemize direk geçiş yapmak için tıklayabilirsiniz.

İlk olarak .NET Framework, .NET Core, .NET Standard’ın ne olduğunu kısaca açıklayalım.

.NET Framework Nedir?

.NET Framework, çalışan uygulamalarına çeşitli hizmetler sağlayan Windows için yönetilen bir yürütme ortamıdır.

.Net Framework 1.0 versiyonu ile 2002 yılında yayınlandı. Şu an ise en son olarak 4.8 versiyonu yayınlanmıştır.

.NET Framework Versiyonları

C# dili ise ilk olarak 2002 yılında yayınlanmıştır. En son C# 7.0 yayınlanmıştır.

C# Versiyonları

.NET Core Nedir?

Microsoft tarafından açık kaynak kodlu olarak geliştirilen, cross platform olarak çalışabilen geliştirme platformudur.

Avantajları

.Net Core’un en çok göze çarpan özelliği işletim sistemi bağımsız çalışabilmesidir. Yani yazdığınız kod Windows, macOS ve Linux işletim sistemlerinde çalışabilmektedir.

IIS bağımlılığının ortadan kalkması: Bir çok geliştiriciden IIS’in muadillerine göre yavaş olduğunu duymuşsunuzdur. .NET Core ile artık IIS bağımlılığı ortadan kalkıyor.

Performansının iyi olması: Firmalarının .NET Core geçiş sebeplerinin başında gelmektedir. .NET Core hafif, bellek kullanımı düşük ve hızlı bir framework’tür. Firmalar .NET Core’a geçerek sunucu maliyetlerini düşürmeyi amaçlamışlardır.

Uygulamadan alınan çıktı ve bellek kullanımı karşılaştırılması (.NET Core 2.2 vs 3.0)
Cevap süresi karşılaştırılması (.NET Core 2.2 vs 3.0)

.NET Core, CLI dediğimiz komut satırı arayüzü ile çalışmaya uyumlu olacak şekilde tasarlanmıştır. Bu sayede artık komut satırı ile proje oluşturup, build edip ayağa kaldırabilmek mümkün.

Bir diğer avantajı ise açık kaynak olarak geliştirilmesidir. Aşağıdaki linkten ulaşabilirsiniz.

.NET Standard Nedir?

  • Tüm .NET uygulamalarının kullanabildiği paketler içeren bir kütüphanedir.
  • Microsoft’un hedefi tek bir .NET Standard kütüphanesinde yazılmış kodun, farklı .NET Platform’larında (.NET Framework, .NET Core, Xamarin, Mono, Windows Phone vs.) çalışmasını sağlayıp, platformlar arası kod paylaşımını arttırabilmekti.
  • .NET Framework’ten .NET Core’a geçiş sürecinde kolaylık sağlar.

ASP.NET Core MVC Nedir?

Asp.Net Core MVC, .Net Core ile birlikte kullanarak web uygulamaları geliştirmemize yarar.

Avantajlarından kısaca bahsedecek olursak:

  • Tüm ayarlamalar Startup.cs içinde yapılıyor. Global.asax yok.
  • Web.config yok onu yerine daha kullanışlı appsettings.json var.
  • Artık sadece belirtilen klasörlere dışarıdan erişilebiliyor default olarak wwwroot klasörüne erişilebiliniyor, ayrıca hangi klasörlerin erişilebileceği özelleştirilebiliyor.
  • Dependency Injection desteği ile birlikte geliyor. 3. parti uygulamalar gerek kalmadan gerçekleştirmemize olanak sunuluyor.
  • Bir isteğin izleyeceği yolu middleware’ler ile tasarlayabiliyoruz.
  • Sade csproj dosyası oluşuyor.
  • Web Api projesi ayrı değil, aynı controller ile yapılabiliyor.

Startup.cs Dosyası

Bütün ASP.NET Core uygulamalarında olmak zorundadır. Middleware ayarlarının ve DI container’ın yapılandırıldığı sınıftır. ConfigureServices ve Configure adında iki metod içermektedir.

ConfigureServices : Kullanılacak servislerin belirtildiği, dependecy injection işlemlerinin yapıldığı metottur.

Configure : HTTP isteklerinin izleyeceği yolunun yapılandırıldığı sınıftır.

Appsettings.json Dosyası

ASP.NET Core Web uygulamalarında klasik MVC projelerindeki web.config’e benzer yapıda çalışan “appsettings.json” dosyası vardır. Uygulamanıza ait yapılandırma ayarlarını, sabit değerleri, connection string’i veya basit yapılandırma anahtarlarını burada barındırabiliriz.

Örnek appsettings.json dosyası

Appsetting.json Dosyasından Veri Okuma

Appsetting.json dosyasına eklediğimiz herhangi bir ayara uygulama içerisinde IConfiguration nesnesi üzerinden ulaşabiliyoruz. Aşağıdaki örnekde appsettings.json dosyasındaki ConnectionStrings altındaki DefaultConnection ayarını okuyoruz.

appsettings.json dosyasından veri okuma

wwwroot Klasörü

.NET Core projesinde statik dosyaların koyulduğu yerdir. Sadece bu klasörün altındaki dosyalara istek yapılabilir. Startup.cs sınıfında Configure methotunda app.UseStaticFiles() demek gerekmektedir. Ayrıca startup.cs dosyasında düzenlemelerle istenilen başka klasörlerde dışarıya açılabilir.

wwwroot klasörünün erişime açılması

wwwroot klasörü dışında herhangi bir klasörü erişime açmak isteyebiliriz. Aşağıdaki örnekte static adlı klasörü erişime açıyoruz ve requestPath de belirttiğimiz adres üzerinden erişebileceğini belirtiyoruz. Artık /static/FILENAME adresine istek yaparak dosyalara ulaşabiliyoruz.

wwwroot dışındaki bir klasörü erişime açma

Dependency Injection Kullanımı

.NET Core MVC dependency injection desteği ile gelmektedir. Dependency injection işlemleri Startup.cs içindeki ConfigureServices metodunda gerçekleştirilir.

.NET Core MVC constructor injection’u desteklemektedir. Property injection için 3.parti paketlere (Autofac vb) ihtiyaç vardır.

3 farklı şekilde injection yapılabilir:

  • Transient: Nesneye yapılan her çağrıda yeni bir nesne oluşturulur.
  • Scoped: Yapılan her request’te nesne tekrar oluşur ve bir request içerisinde sadece bir tane nesne kullanılır.
  • Singleton: Uygulama başlatıldığında register edilen nesneye ait sadece bir tane instance oluşur ve uygulamadaki her yerden bu referans çağrılır.
Örnek dependecy injection ayarları

HTTP Request Pipeline ve Middleware Kavramı

.NET Core’da MVC ile birlikte uygulamaya gelen isteklerin izleyeceği yolu tasarlayabiliyoruz. Gelen istekleri middleware ile karşılayıp istenilen işlemleri, doğrulamaları yapabiliyoruz. Örneğin istek yapılan action’a istek yapan kişinin yetkisi olup olmadığını bir middlewarede kontrol edip o middleware’den hata döndürerek yada başka action’a yönlendirerek ulaşmasını engelleyebiliriz.

  • Middleware bileşenleri ile uygulamanın pipeline’ını belirliyoruz.
  • Uygulama başlangıcında eklenen middleware bileşenleri, eklendiği sıraya göre pipeline’ı oluşturmakta ve bir request geldiğinde bu sıraya göre tüm bileşenler çalışmaktadır.
HTTP Request Pipeline

Custom Middleware Yazımı

Şimdi örnek bir middleware yazarak request pipeline’ına koyalım.

CustomMiddleware adında bir class oluşturuyorum. Bu sınıfın yapıcı metodu RequestDelegate tipinde bir parametre almak zorundadır. Bu parametre sonraki middleware’in bilgilerini içermektedir. (Bunu .NET Core sağlıyor, herhangi bir şey yapmamıza gerek yok)

Gelen istek middleware girdiğinde Invoke adındaki metodu çalıştırmaktadır. Bu metot içerisinde RequestDelegate tipindeki nesnenin Invoke metodu çağrılmalıdır. Bunun amacı sonraki middleware i çalıştırmaktır. Bu metot çağrılmadan önceki kısım request zamanında çalışırken sonraki kısım ise response zamanında çalışmaktadır.

Middleware i yazdıktan sonra Startup.cs dosyası içerisindeki configure metodunda tanımlamamız gerekmektedir. Burada tanımladığımız sıraya göre gelen istek ilgili middlewarelere girmektedir.

Middleware Tanımlanması

Csproj Dosyası

.NET Framework ile bir web projesi açtığımızda varsayılan olarak oluşan csproj sayfası bir çok bilgi içermekteydi. Karışık ve anlaşılması zordu.

Aşağıda yeni bir .NET Framework projesi açtığımızda gelen csproj dosyası bulunmaktadır ve 223 satır 😃

.Net Framework’te varsayılan MVC csproj dosyası

Aşağıda ise yeni bir .NET Core MVC projesi açtığımızda oluşan csproj dosyası bulunmaktadır.

Sadece target framework bilgisini içermektedir, Microsoft.EntityFrameworkCore paketini örnek olması açısından ekledim

.NET Core MVC csproj dosyası

.NET Core MVC yapısından bahsettikten sonra gelen bazı yeni özelliklerden bahsetmek istiyorum.

View Components

.NET Core ile birlikte partial view’lara ek olarak view component’ler geldi. Partial View’lara ulaşmak için controller katmanıyla her defasında iletişim kurmak zorunda kalınıyordu ve bu durum maliyetli oluyordu. View component’ler ile controllerlarla iletişime gerek kalmadan işlemin gerçekleştirip view’a ulaşılması sağlandı. View Component’lar, dışarıdan http istek ile doğrudan ulaşılamazlar. Klasik MVC’de partial viewler URL’den ulaşılabiliyorken, View Componentl’ar URL’den ulaşılamaz.

View Component Sınıfı, aşağıdaki yollardan biri ile oluşturulabilir;

  1. “ViewComponent” adlı abstract bir sınıftan türetilerek
  2. Sınıfın isminin sonuna “ViewComponent” ekleyerek
  3. Sınıfı“ViewComponent” attribute ile işaretleyerek

ViewComponentlerin view kısımları aşağıdaki gibi Shared klasörününde ViewCompenintinin ismindeki klasörün altına konulabilir.

Örnek ViewComponent Yazımı

ViewComponent sınıfından türetilmiş Example adında sınıf oluşturuyorum. Bu sınıfta Invoke metodu olmak zorundadır. ViewComponentini çağırdığımızda çalışır. Aşağıdaki örnekte bir liste dönüyorum.

Example adında ViewComponent

Invoke metodunda dönen nesneyi listeledim.

Oluşturduğumuz ViewComponentleri aşağıdaki gibi istenilen cshtml dosyasından çağırabiliriz.

İlk parametre ViewComponentin ismi, ikinci parametre ise göndermek istediğimiz parametreler. Parametresizde çalışabilmektedir.

Razor Pages

Razor Pages, WebForms gibi hızlı ve kolay uygulama geliştirmek için kullanılan yeni bir web uygulama geliştirme yöntemidir. Razor Pages işlemler yapmak için Model yapısını kullanır. Model yapısını kullanmak için @model ile razor sayfasına dahil edilir. Bu model genellikle razor sayfası ile aynı ada sahip .cshtml.cs dosyalarında yer alır. Model içerisine eklenen özellikler daha sonra razor sayfalarında kullanılabilir. İsteklerin yönetimi;

  • Sayfaya yapılan istekleri (GET, POST) yönetmek için handler yapısı kullanılır.
  • Örneğin GET istekleri için OnGet veya OnGetAsync metodu çalışır. Diğer istekler için de aynı şekilde çağrılmaktadır.

Örnek Razor Page Eklenmesi

RazorPages template ini seçip açtığımız uygulamada aşağıdaki gibi bir yapı bizi karşılamaktadır. Pages klasörü altında sayfalar ve bu sayfaların isteklerinin, modelinin yönetildiği cs dosyası bulunmaktadır.

Razor Pages Template

Sayfaların isteklerinin yönetildiği sayfa PageModel sınıfından türetilmelidir. Bu sayfadaki propertylere direk olarak cshtml dosyasında Model üzerinden ulaşılabilmektedir. Örneğin aşağıdaki message property’sine cshtml dosyasında Model.Message diyerek ulaşabiliyorum.

Buradaki OnGet ve OnPost metotları ilgili isteklerde çalışır. Çalıştıktan sonra sayfa render olur. Örneğin OnGet metodu sayfaya get isteği yapıldığında çalışır ve sayfa render edilir.

Razor Page

Sayfadaki form ile post isteği geldiğinde ise OnPost metodu çalışır. Eğer sayfada birden fazla form olursa asp-page-handler attribute’ü ile verilen isme göre ilgili metoda düşmektedir.

Örneğin asp-page-handler attribute’ü “edit” olan form post olduğunda “OnPostEdit” metodu çalışacaktır.

Aşağıda birden fazla form içeren razor page örneği bulunmaktadır.

Birden fazla form içeren razor page

Aşağıda ise bu formların düştüğü metotlar bulunmaktadır.

Birden fazla form içeren razor page modeli

Blazor

Web uygulamalarının Blazor adında bir .NET Web Framework’ü ile browser üzerinde çalıştırılmasıdır. Kısaca backend’e gerek kalmadan, frontend tarafta yazılan C# kodlarının browser tarafında derlenip çalıştırılmasıdır. Kodları browser tarafında çalıştırmak için WebAssembly kullanılmaktadır. WebAssembly sayesinde, herhangi bir web tarayıcısı, ilgili dilleri yani yazılan kodları ve import edilen dll’leri derler.

Peki Sonrası: .Net 5 😍

Microsoft’un yazılan kodun tüm platformlarda çalışması amacıyla Kasım 2020 de çıkartmayı planladığı framework’tür.

.NET 5
.Net Schedule

Peki .NET Core’a Neden Geçiyoruz? 😕

.NET Core’a geçmemizin temel nedenleri:

  • .NET Core, performans bakımından testlerde en hızlı frameworklerden. Bellek kullanımı düşük ve hafif, bu da server maliyetlerini ciddi anlamda düşürüyor.
  • Yeni teknolojilere ve paketlere uyumluluk. Artık yeni paketler veya teknolojiler .NET Core’a uyumlu çalışacak şekilde geliştiriliyor.
  • Cross platform avantajı.

Geçiş Süreci

Uygulamalarımızı .NET Core’a geçirmeden önce şirketimizdeki uygulamaların kullandığı ortak framework projelerimizi .NET Core’a hazır hale getirmemiz gerekiyordu. Şirket için framework solution’ımız 6 tane proje içeriyordu. İlk düşüncemiz .NET Standard’a geçirip hem .NET Core hem .NET Framework’te çalışmasını sağlamaktı. Ancak 3. parti bağımlılıklardan ve ihtiyaçlardan dolayı .Net Standard ile yazamadık. Bundan dolayı projeleri multi target olarak yani hem .NET Framework hem de .NET Core’a göre çalışabilir olarak geliştirdik.

Multi Target Çalışma

Projelerin birden fazla hedef framework’e göre yazılmasıdır.

Projenizi multi target yapmak için sadece csproj da hedef framework’leri belirtmeniz yeterli olacaktır.

Multi Target - csproj düzenlemesi

if else blokları ile eğer proje .NET Framework ile çalışıyorsa farklı bir kod .NET Core ile çalışıyorsa farklı bir kod bloğunun çalışmasını sağladık. Aynı şekilde uygulamaların csproj dosyalarını condition ile hedef framework’e uygun hale getirdik.

Kod bloklarının if else ile frameworke göre çalışması
Multi Target — csproj paketlerin hedef kütüphaneye göre eklenmesi

Yaşanan Zorluklar 😢

Geçiş sürecinde en çok yaşadığım zorluk değişen library ve namespaceler idi.

Örneğin, aşağıdaki aynı işi yapan iki kod var. Propertylerin display name’leri o anki dile göre gösterilmesini sağlanıyor. Aynı işlemi yapmasına rağmen içerikleri farklı.

.NET Framework tarafındaki kodumuz
.NET Core tarafındaki kodumuz

HttpContext Değişikliği

Eskiden olduğu gibi uygulamanın her yerinden HttpContext’e artık ulaşılamıyor. Ancak inject edilen HttpContextAccessor üzerinden ulaşılabiliyor.

Ulaşabilmek için aşağıdaki gibi IHttpContextAccessor’u inject etmemiz gerekiyor.

IHttpAccessor Injection
IHttpContext üzerinden HttpContext’e erişilmesi

Session’a Objelerin Atılamaması

.NET Core’da session’a Int32, string ve byte[] tipindeki nesneler atılıp okunulabiliyordu. Ancak objeler atılamıyordu. Çözüm olarak objeleri json string’e çevirip string olarak attık. Okurken ise json string’i objeye çevirerek aynı şekilde çalışmasını sağladık.

Objelerin json obje olarak atılıp okunması

Entity Framework Core’da SqlQuery Olmaması

Entity Framework’te herhangi bir SQL çalıştırılıp sonuç belirtilen nesne olarak alınabiliyordu ancak EF Core’da SQL sorgusu çalıştırılıp direk bir nesneye atılamıyordu. Projelerde çokça kullanıldığından böyle bir yapıya ihtiyacımız vardı.

EF’de kullanım şekli

Entity Framework’de SqlQuery Kullanım Şekli

EF Core’da SQL komutunu ExecuteReader ile çalıştırıp sonuca satır satır ulaşabiliyorduk. Satırları okuyup kolonları istenilen nesnenin propertylerine manuel olarak map edip aynı şekilde sonuç döndürebildik.

Çözüm

DependencyResolver Olmaması

  • .NET Framework’te statik Container adlı sınıfda DependencyResolver arayıcılığıyla inject edilmiş nesnelere her yerden ulaşılabiliyordu ancak .NET Core’da DependencyResolver yoktu.
  • .NET Core’da ise HttpContext üzerinden GetService metodu ile nesnelere ulaşabiliyoruz.
  • Container sınıfı statik olduğundan ve HttpContext’e de HttpContextAccessor üzerinden ulaşıldığından bir şekilde HttpContextAccessoru static sınıfa ulaştırmak gerekiyordu.

Configure metodunda inject edilmiş HttpContextAccessor nesnesini alıp statik sınıfın property’sine attıktan sonra HttpContext’e ulaşabildik.

HttpContextAccessor nesnesinin Container sınıfının property’sine atılması
Container Sınıfı

Göç hikayemiz devam ediyor. Yeni ürünlerimizi .NET Core ile yazmaya başladık, mevcut ürünlerimizi dönüştürmeyi planlıyoruz.

Yeni hikayelerde buluşmak üzere…

--

--