Asp.Net Core 5.0'da FluentApi ile Code-First Yaklaşımı

Berk Garip
HBK SOFTWARE
Published in
7 min readMar 3, 2021

--

Herkese Merhabalar,

İlk yazımızda sizlere Entity Framework Core Code First yaklaşımında ilişkisel tablo ve veri tabanı tasarımından bahsetmeye çalışacağız.

Öncesinde Entity Framework Core nedir onu inceleyelim;

Entity Framework Core : EF Core 2016 yılından itibaren .Net Core platformu
için geliştirilen bir ORM (Object Relational Mapping) aracıdır. ORM, bizim nesneye yönelik olan kod bloklarımız ile veri tabanımız arasında oluşan bir köprüdür.

Burada dikkat etmemiz gereken bir husus var. Code First hakkında araştırma yaptığınız zaman bulacağınız kaynaklar Entity Framework üzerinden anlatılmış olabilir. Entity Framework ile Entity Framework Core
arasında fark olduğunu, ikisinin de aynı şeyler olmadığını anlamanızı isterim.
Biz burada Entity Framework Core kullanacağız. Bunun en önemli sebebi ise verimli, güncel ve açık platform desteği olan bir yapı olmasıdır.

EFCore’da 3 farklı şekilde proje geliştirebilirsiniz. Bunlar;

1- Database-First(Önce Veri Tabanı)

2- Model-First(Önce Model)

3- Code-First(Önce Kod)

Dilerseniz .Net Core MVC web projemizi ve class library projemizi oluşturalım.

Asp.Net Core MVC Web Projesi
Asp.Net Core Class Library Projesi

Oluşturduğumuz her iki projeye Package Manager Console üzerinden
Entity Framework Core ve SqlServer paketlerini,

Install-Package Microsoft.EntityFrameworkCore -Version 5.0.0 ,

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.0

komutları ile projelerimize ekliyoruz.

Entity Framework Core Paketinin Yüklenmesi
SqlServer Paketinin Yüklenmesi

Yapımızı oluşturduk, ilgili paketleri yükledik ve tablo yapılarımızı oluşturmaya hazırız. 4 adet tablo oluşturup: bire-bir, bire-çok, çoka-çok ilişkilerinin hepsini bu tablolar üzerinden anlatmaya çalışacağım.

Employee(Çalışan), Department(Departman), EmployeeAdress(Çalışan Adres), Project(Proje), EmployeeProject(Çalışan Projeleri) modellerimizi oluşturup, ilgili değişkenleri tanımlamaya başlayalım.

Employee.cs
Department.cs
Project.cs
EmployeeAdress.cs

Modellerimizi oluşturduk .İlişkilerimizi oluştururken FluentAPI’dan yararlanacağız. Fluent API kullanmadan da ilişkilerimizi kurabiliriz fakat burada işi tamamen Entity Framework Core’a bırakmış oluyoruz ki
büyük projelerde EFCore’un ilişkileri yanlış eşleştirmesi ve ForeignKey’lerin hatalı ayarlaması çok olası bir durum.

FluentAPI’ın bize sağladığı Has/With deseni ile tablolarımız arasında ilişkilerimizi oluşturmaya başlayalım. İlişkiler tanımlanırken Has (sahip olduğumuz) tarafı ilişkinin kurulduğu tarafı temsil eder ve HasOne/HasMany metodları ile ilişkiyi tanımlamamıza olanak sağlar .With (ile) tarafı ise ilişkinin kurulacağı tarafı temsil eder ve WithOne/WithMany ile ilişkinin diğer tarafını tanımlamamıza olanak sağlar. Geçerli bir ilişki yapılandırabilmemiz için Has ve With metodlarını birleştirmeliyiz.

Bire-bir (1–1) ilişkisi :
Oluşturduğumuz Employee ve EmployeeAdress sınıfları arasında olan ilişkidir.

Employee.cs ve EmployeeAddress.cs sınıflarımızdaki ilişkileri yapılandıralım.

Employee.cs ve EmployeeAddress.cs sınıfları arasındaki ilişkinin tanımlanmış

Yukarıda gördüğünüz gibi ilişkimizi tanımladık. Burada Has/With desenlerimize göre gezinme özellikleri tanımladım. İlişkimiz bire-bir olduğu için direkt olarak ilgili sınıfın adını yazdık ve Fluent API’yla beraber bu sınıfı kullandık. Employee tablomuzdaki EmployeeID bir PrimaryKey (birincil anahtar) ama bu ilişkinin bir tane de ForeignKey’e (yabancı anahtar) ihtiyacı var. EmployeeAdress tablosundaki AddressID nesnesinin bir ForeignKey olarak belirttik. Diğer ilişkileri tanımlarken hep tek bir taraftan tanımladık fakat burada iki taraftan da belirtmek zorundayız. Sebebini ise şöyle açıklayalım : Employee tablosundaki EmployeeAddress’leri Linq ile Include etmek istiyorsunuz diyelim. Aynı Include işlemini EmployeeAddress tablosundan Employee’leri çağıracak şekilde yaparsanız, sorgunuz size null değer döndürür. Çünkü tek bir taraftan tanımladığınız zaman sadece o taraf üzerinden Include işlemini gerçekleştirebilirsiniz. Bundan dolayı da ilişkiyi her iki tarafta da tanımlama ihtiyacı duyuyoruz.

Bire-çok (1-n) ilişkisi:

Oluşturduğumuz Department ve Employee sınıfları arasında olan ilişkidir.
Departman.cs ve Employee.cs sınıflarımızdaki ilişkileri yapılandıralım.

Departman.cs ve Employee.cs arasında ilişkilerin tamamlanmış hali.

Yukarıda gördüğünüz şekilde ilişkiyi tanımladık. Bire-birden farklı olarak burada bir koleksiyon gezinti özelliği ekleyeceğiz. Böylece Employee sınıfı birden fazla Project sınıfından nesneye erişebilir ve ilişkimizi tanımlamış oluruz. Adım adım anlayalım.

1- builder.HasOne(e => e.Department) ile ilişkinin ilk ucunu tanımlıyoruz
2- .WithMany(e => e.Employees) ile ilişkinin çoklu gezinme özelliğini seçtik.
3- .HasForeignKey(e => e.DepartmentID) ile ilişkinin bir tarafında olması gereken ForeignKey’i (yabancıl anahtarı) belirttik.
4- .OnDelete(DeleteBehavior.SetNull); ile de ilgili bir verinin silinmesi durumunda bağlantılı verilerin Null (hükümsüz) atanmasını sağlıyoruz.

Veri silme durumlarında bu ilişkinin nasıl bir davranış sergilemesi
gerektiğini ayarlayabilirsiniz. Cascade, Restrict, NoAction gibi seçenekler mevcut. Daha fazla bilgi almak için
bu linke tıklayabilirsiniz.

Çoka-çok (n-n) ilişkisi:
Oluşturduğumuz Employee ve Project sınıfları arasında olan ilişkidir.

Bu ilişkide aklımıza HasMany/WithMany yapmak gelebilir ama büyük projelerde yönetilebilirliğin artması için bu ilişkiye özel bir tablo oluşturup, çoka-çok olan ilişkiyi 2 adet bire-çok ilişki şeklinde bölebiliriz.
Oluşturacağımız yeni tabloda, Employee ve Project tablolarının PrimaryKey’lerini(birincil anahtarlar) de bu tabloya çekip, ilişkilerini de yeni oluşturduğumuz bu tablo üzerinden HasOne/WithMany olarak tanımlayabiliriz.

Çoka-çok ilişkinin ilk kısmı.
Çoka-çok ilişkinin ikinci kısmı.

Yukarıda görmüş olduğunuz üzere iki adet bire-çok (1-n) ilişki tanımlayarak dolaylı yoldan bir çoka-çok ilişkisi tanımlamış olduk.

İlişkilerimizi tanımladık fakat bu ayarların hepsi özelleştirilmiş bir ayarlar bütünü. Projemizdeki veri tabanına gidip bu ayarları belirtmeliyiz ki ona göre özelleştirilebilsinler.

İlk önce bir sınıf oluşturup veri tabanımızdaki bütün tabloları bu sınıf altında toplayacağız. Daha sonrasında bu sınıfın bir veri tabanı sınıfı olduğunu
belirtmek için oluşturduğumuz sınıfı DbContext sınıfından miras alacağız. Miras aldıktan sonra da oluşturmuş olduğumuz özelleştirilmiş ayarlarımızı bu sınıfta tanımlayacağız.

Projemizin DbContext sınıfı.

Yukarıda görmüş olduğunuz gibi sınıfımızı oluşturduk ve DbContext sınıfından miras aldık. Turuncu alanda ise oluşturduğumuz tabloları tanımladık. Hemen altındaki kırmızı alanda ise ilişkilerimizin nasıl özelleştirileceğini belirten ayarlar bütünlerini tanımladık.

Şimdi de bu veri tabanı sınıfını projemize bildirmeliyiz ki proje böyle bir veri tabanına sahip olduğundan haberdar olsun. Bu aşamayı projeyi ayağa kaldırma aşaması gibi de düşünebiliriz .Web projemiz içerisinde bulunan appsettings.json dosyamız içerisinde connectionString adresimizi belirtelim.

connectionString adresimiz.

Startup.cs sınıfımız içerisinde de bu connectionString’i kullanarak bir veritabanı içeriği ekleyelim.

Startup.cs sınıfındaki işlemler.

Tüm bu ayarlamaları yaptıktan sonra projemizi çalıştırmaya bir adım kaldı. EF Core Entity projemizdeki veri tabanı içeriklerimizin veri tabanımıza yansıtılması lazım.Migration oluşturarak bu işlemi gerçekleştireceğiz.

Nuget Package Manager Console’dan ilk önce default (varsayılan) proje olarak EFCoreEntity projemizi seçiyoruz.

Migration eklenmesi.

Daha sonra da konsoldan Add-Migration “migration ismi” komutumuzu çalıştırıyoruz. Son olarakta Update-Database komutumuzu çalıştırarak ilgili bütün işlemleri bitirmiş oluyoruz.

EfCoreWebMvc projemizi başlangıç projesi seçtikten sonra çalıştırabilir ve veri tabanımız üzerinden tablolar arası ilişkileri görüntüleyebiliriz.

Veri tabanımıza yansıtılan Primary(öncelikli) ve Foreign(yabancı) anahtarlarımız.

Code-First yaklaşımını gerçekleştirmiş olduk. Bu yaklaşım bize neler kazandırıyor diye düşünebilirsiniz.

Buyrun, avantajlarından bahsedelim;
1- Database First yaklaşımı ile yapılan her şey yapılabilir ve kullanımı basittir.
2- Yönetmeniz gereken ve sürekli büyüyen bir edmx modeliniz yoktur.
3- Dizayn desenleriyle(Desing Pattern) beraber kullandığınız zaman projenin büyük bir tarafında denetime sahip oluyorsunuz.
4- Migration dosyaları sayesinde veri tabanının versiyonları kolaylıkla saklanabilir, istenilen versiyona veri tabanı yapısına uyumlu şekilde geri dönülebilir.
5- Küçük model değişiklikleri herhangi bir veri kaybına neden olmaz.
6- Daha fazla özelleştirme seçeneğine sahipsiniz.
7- Varlıklarınızı istediğiniz gibi kolay bir şekilde, özgürce tasarlayabilirsiniz.
8- Modellemede hata oluştuğunda Entity Framework Core bunu size hemen bildirecektir.

Elbette bu kadar çok avantajdan bahsettikten sonra kuşkulanmış olabilirsiniz, biraz da dezavantajlarından bahsedelim;

1– Görsel bir arayüz olmadığı için veri tabanını kodlar üzerinden yönetmek sizi zorlayabilir.
2- Veri tabanını oluşturabilmek için C# bilmeniz gereklidir.

Peki Code-First Yaklaşımını Hangi Durumlarda Kullanmalıyız :

Örnek bir karar şeması.

Yukardaki örnek şemayla beraber devraldığınız projelerdeki veri tabanı tasarımına göre de karar verebilirsiniz.

Veri tabanı bilgisine sahip olmadan proje geliştirmek istediğinizde kullanılabilir.

Görsel bir veri tabanı arayüzü kullanmak istemediğinizde kullanılabilir.

Veri tabanı sık sık değişiyorsa kullanılabilir.

Veri tabanı kısa ömürlü ise kullanılabilir.

Veri tabanının daha kontrol edilebilir olmasını istediğinizde kullanılabilir.

Çalışmanın tamamlanmış haline buradan ulaşabilirsiniz .

Yararlı olması dileğimle..

Berk GARİP

Kaynaklar:

--

--