Oğuzhan KARAGÜZEL

OĞUZHAN KARAGÜZEL

Yazılım geliştirici

EF Core Nedir? Ortam Hazırlığı ve DB First Yaklaşımı

Yazar: Oğuzhan KARAGÜZEL ·

Entity Framework Core (EF Core) ile veritabanı katmanını nasıl kuracağınız, projenin ilk gününde verdiğiniz en önemli kararlardan biridir: Önce veritabanını mı tasarlayacaksınız, yoksa önce C# sınıflarını mı yazacaksınız? Bu üç bölümlük seride üç yaklaşımı (DB First, Code First ve Mixed) uygulamalı olarak karşılaştırıyoruz. Bu ilk yazıda EF Core'un ne olduğunu, çalışma ortamını nasıl hazırlayacağınızı ve mevcut bir veritabanından DB First yaklaşımıyla nasıl kod üreteceğinizi adım adım anlatıyorum.

EF Core nedir?

EF Core, Microsoft'un geliştirdiği, açık kaynaklı ve çok platformlu bir ORM (Object-Relational Mapper) aracıdır. Tablolarla doğrudan SQL yazarak uğraşmak yerine, tabloları C# sınıfları olarak temsil eder ve sorguları LINQ ile yazmanızı sağlar. Başlıca özellikleri şunlardır:

  • Çok platform: Windows, Linux ve macOS üzerinde .NET ile çalışır.
  • Performans: EF Core, eski Entity Framework 6'ya göre belirgin biçimde daha hızlıdır ve her sürümde iyileştirilmeye devam eder.
  • Kolay entegrasyon: ASP.NET Core, bağımlılık enjeksiyonu ve diğer .NET bileşenleriyle doğal şekilde çalışır.
  • Zengin özellik seti: LINQ sorguları, otomatik değişiklik izleme (change tracking), ilişkisel veri desteği ve migration mekanizması sunar.

EF Core birçok veritabanı sağlayıcısını destekler. Bu seride SQL Server (MSSQL) kullanıyoruz; örnekler başka sağlayıcılarda da küçük değişikliklerle çalışır.

Üç yaklaşıma kısa bakış

DB First, veritabanı zaten varsa kullanılır; EF Core veritabanını okuyup C# sınıflarını üretir. Code First, önce sınıfları yazıp veritabanını migration'larla üretme yaklaşımıdır. Mixed ise ikisini birleştirir: mevcut veritabanını koda çeker, sonrasında değişiklikleri migration'larla yönetir. Code First ve Mixed konularını serinin sonraki yazılarında ele alıyoruz.

Hazırlık: SQL Server, DBeaver ve konsol uygulaması

Serideki tüm örnekleri aynı altyapı üzerinde deneyebilmek için önce ortamı kuralım. İhtiyacımız olanlar: bir SQL Server örneği, bir veritabanı istemcisi ve Visual Studio. Ben SQL Server için Docker konteyneri, veritabanını izlemek ve SQL yazmak için de DBeaver kullanıyorum; siz doğrudan kendi SQL Server kurulumunuzu da kullanabilirsiniz, sonuç aynıdır.

1. Docker ile SQL Server

Docker Desktop kuruluyken aşağıdaki komutla SQL Server konteynerini başlatabilirsiniz. Şifre büyük harf, küçük harf, rakam ve özel karakter içermeli ve en az 8 karakter olmalıdır. Kullanıcı adı olarak sa (system administrator) kullanılır.

Docker
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=<GucluBirSifre>" \
  -p 5555:1433 --name sqlserver-efcore \
  -d mcr.microsoft.com/mssql/server:2022-latest

Konteyner birkaç saniye içinde ayağa kalkar. Örneklerde localhost,5555 adresini kullanacağız.

2. DBeaver ile bağlantı

DBeaver'da üst menüden Database → New Database Connection seçeneğini açın, listeden SQL Server'ı seçin ve Next deyin. Sunucu adresine localhost, portuna 5555, kullanıcı adına sa ve belirlediğiniz şifreyi girip Finish ile bağlantıyı tamamlayın. Artık SQL yazıp tablo oluşturabilir, veritabanını izleyebilirsiniz.

3. Konsol uygulaması ve NuGet paketleri

Visual Studio'da yeni bir Console App projesi oluşturun (hedef: .NET 8 veya üzeri). EF Core için dört NuGet paketi kuruyoruz:

.NET CLI
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.Design

Tools paketi Package Manager Console komutlarını (Scaffold-DbContext, Add-Migration vb.), Design paketi ise tasarım zamanı işlemlerini sağlar. Paketler yüklendiyse hazırız.

DB First nedir?

DB First yaklaşımında önce veritabanı yapısı oluşturulur, ardından EF Core bu yapıya uygun entity sınıflarını ve DbContext sınıfını otomatik üretir. Mevcut bir veritabanıyla çalışmak zorunda kaldığınızda ya da veritabanını ayrı bir ekip yönettiğinde en doğal seçenektir.

Örnek veritabanı: okul

Üç ilişki türünü de içeren basit bir veritabanı kuralım: öğretmen–sınıf (bire bir), sınıf–öğrenci (bire çok) ve öğrenci–kulüp (çoka çok). Aşağıdaki script'i DBeaver'da çalıştırın:

SQL
CREATE DATABASE okul;
GO
USE okul;
GO

CREATE TABLE Ogretmen (
    OgretmenID INT PRIMARY KEY IDENTITY(1,1),
    Ad NVARCHAR(50) NOT NULL,
    Soyad NVARCHAR(50) NOT NULL,
    DogumTarihi DATE,
    Brans NVARCHAR(50)
);

CREATE TABLE Sinif (
    SinifID INT PRIMARY KEY IDENTITY(1,1),
    SinifAdi NVARCHAR(50) NOT NULL,
    OgretmenID INT UNIQUE, -- UNIQUE: bire bir iliski
    FOREIGN KEY (OgretmenID) REFERENCES Ogretmen(OgretmenID)
);

CREATE TABLE Ogrenci (
    OgrenciID INT PRIMARY KEY IDENTITY(1,1),
    Ad NVARCHAR(50) NOT NULL,
    Soyad NVARCHAR(50) NOT NULL,
    DogumTarihi DATE,
    SinifID INT, -- bire cok iliski
    FOREIGN KEY (SinifID) REFERENCES Sinif(SinifID)
);

CREATE TABLE OgrenciKlupleri (
    KlupID INT PRIMARY KEY IDENTITY(1,1),
    KlupAdi NVARCHAR(50) NOT NULL
);

-- Coka cok iliski icin ara tablo
CREATE TABLE OgrenciKlup (
    OgrenciID INT,
    KlupID INT,
    PRIMARY KEY (OgrenciID, KlupID),
    FOREIGN KEY (OgrenciID) REFERENCES Ogrenci(OgrenciID),
    FOREIGN KEY (KlupID) REFERENCES OgrenciKlupleri(KlupID)
);
SQL
-- Once ogretmenler, sonra siniflar (OgretmenID dolu olsun)
INSERT INTO Ogretmen (Ad, Soyad, DogumTarihi, Brans) VALUES
('Ali', 'Yilmaz', '1975-05-15', 'Matematik'),
('Ayse', 'Demir', '1980-08-22', 'Fizik');

INSERT INTO Sinif (SinifAdi, OgretmenID) VALUES
('10A', 1),
('10B', 2);

INSERT INTO Ogrenci (Ad, Soyad, DogumTarihi, SinifID) VALUES
('Ahmet', 'Kaya', '2005-01-12', 1),
('Mehmet', 'Celik', '2004-03-17', 2);

INSERT INTO OgrenciKlupleri (KlupAdi) VALUES
('Bilim Kulubu'),
('Spor Kulubu');

INSERT INTO OgrenciKlup (OgrenciID, KlupID) VALUES
(1, 1), (1, 2), (2, 1);

Not: Sınıfları öğretmenlerden sonra ekliyoruz; böylece her sınıfın OgretmenID değeri dolu oluyor ve bire bir ilişkiyi gerçekten test edebiliyoruz.

Scaffold-DbContext ile kod üretimi

Mevcut veritabanından C# kodu üretmek için Scaffold-DbContext komutu kullanılır. Visual Studio'da Tools → NuGet Package Manager → Package Manager Console penceresini açıp aşağıdaki komutu kendi bağlantı bilgilerinize göre düzenleyerek çalıştırın:

Package Manager Console
Scaffold-DbContext "Server=localhost,5555;Database=okul;User Id=sa;Password=<SIFRE>;TrustServerCertificate=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities -ContextDir Context -Context OkulContext

Komutu parçalarına ayıralım:

  • Scaffold-DbContext: Var olan veritabanına tersine mühendislik uygulayarak C# sınıflarını üretir.
  • Bağlantı dizesi (connection string): Sunucu, veritabanı adı ve kimlik bilgileri. Kendi ortamınıza göre değiştirin.
  • Microsoft.EntityFrameworkCore.SqlServer: Kullanılacak veritabanı sağlayıcısının NuGet paket adı.
  • -OutputDir: Tablolara karşılık gelen entity sınıflarının yazılacağı klasör.
  • -ContextDir: DbContext sınıfının yazılacağı klasör. Klasör adı verilmezse tüm sınıflar proje köküne dağılır; bu yüzden klasör adı vermenizi öneririm.

Komut çalışınca EF Core veritabanını inceler; tablolar için entity sınıflarını, ilişkiler için gerekli yapılandırma kodunu ve bir DbContext sınıfını projenize ekler.

Faydalı ek parametreler

  • -Force: Daha önce üretilmiş dosyaların üzerine yazar. Veritabanı değiştikten sonra komutu yeniden çalıştırırken gerekir.
  • -Tables: Yalnızca belirtilen tabloları üretir.
  • -NoPluralize: Çoğul/tekil dönüşümünü kapatır (aşağıda neden işe yaradığını göreceksiniz).
  • -NoOnConfiguring: Bağlantı dizesinin üretilen DbContext içine yazılmasını engeller.

Visual Studio kullanmıyorsanız aynı işi .NET CLI ile yapabilirsiniz (önce dotnet tool install --global dotnet-ef):

.NET CLI
dotnet ef dbcontext scaffold "Server=localhost,5555;Database=okul;User Id=sa;Password=<SIFRE>;TrustServerCertificate=True" Microsoft.EntityFrameworkCore.SqlServer --output-dir Entities --context-dir Context --context OkulContext --no-onconfiguring

Üretilen kodu çalıştırma

Kodun çalıştığını görmek için öğretmen adlarını konsola yazdıralım:

C#
using var context = new OkulContext();

List<Ogretman> ogretmenler = context.Ogretmen.ToList();

foreach (Ogretman ogretmen in ogretmenler)
    Console.WriteLine($"Öğretmen : {ogretmen.Ad} {ogretmen.Soyad}");

Program çalıştığında veritabanındaki iki öğretmenin adı listelenir. Diğer CRUD işlemlerini (ekleme, güncelleme, silme) de aynı DbContext üzerinden deneyebilirsiniz.

Burada dikkatinizi çekebilecek bir ayrıntı var: tablo adı Ogretmen iken sınıf adı Ogretman olarak üretildi. Bunun nedeni EF Core'un İngilizce kurallara göre çalışan çoğul/tekil dönüştürücüsüdür (pluralizer); Türkçe adlandırılmış tablolarda bu tür beklenmedik sonuçlar çıkabilir. DbContext içindeki DbSet özelliğinin adı ise farklı kalabilir. Bu karmaşadan kaçınmak için tablo ve sütun adlarını İngilizce vermeyi ya da -NoPluralize kullanmayı düşünebilirsiniz.

Çoka çok ilişkiler nasıl üretilir?

EF Core 5'ten itibaren yalnızca iki foreign key'den oluşan ara tablolar (örneğimizdeki OgrenciKlup) ayrı bir entity olarak değil, iki taraftaki koleksiyon özellikleri (skip navigation) olarak üretilir. Yani Ogrenci sınıfında doğrudan kulüp koleksiyonunu, kulüp sınıfında da öğrenci koleksiyonunu görürsünüz; ara tabloyla elle uğraşmanız gerekmez. Ara tabloya ek sütun koyarsanız (örneğin üyelik tarihi) EF Core onu normal bir entity olarak üretir.

DB First'ün avantajları ve dezavantajları

Avantajlar

  • Mevcut veritabanı yapısını hızlıca koda dönüştürür.
  • Veritabanı mimarisiyle birebir uyumludur.
  • Karmaşık veritabanı yapılarında bile kolay kullanım sağlar.

Dezavantajlar

  • Kod üzerinde yaptığınız değişiklikler veritabanına otomatik yansımaz.
  • Veritabanı her değiştiğinde scaffold işlemini yeniden yapmanız gerekir; klasör parametrelerini unutmak projede karışıklığa yol açabilir. Üretilen sınıfları elle düzenlediyseniz yeniden üretimde bu değişiklikler kaybolur (partial class kullanmak bunu hafifletir).
  • Veritabanı yapısına üst düzey bağımlılık nedeniyle esneklik azalır.
  • Veritabanındaki değişiklikleri izlemek, migration geçmişine sahip bir Code First projesine göre daha zordur.

Ne zaman DB First?

Projenizde veritabanı uzmanları çalışıyorsa veya veritabanı size hazır olarak veriliyorsa DB First çoğu zaman en doğru seçimdir. Hazır bir veritabanı için entity sınıflarını elle yazmak hem zahmetlidir hem hataya açıktır. Veritabanını sizin sıfırdan tasarlayacağınız bir projede ise Code First genellikle daha rahattır; çünkü ilişkiler için gereken tabloları SQL ile elle yönetmek yerine kodla tanımlarsınız.

Serinin devamında Code First ve Migration yönetimi konusunu işliyoruz.