C# ve .NET [23]
Entity Framework Core Temelleri: .NET’te Veritabanı Etkileşimini Yeniden Şekillendirmek Giriş: Veritabanı Erişiminin Evrimi ve ORM İhtiyacı Modern uygulamaların büyük çoğunluğu, verileri kalıcı olarak saklamak ve yönetmek için bir veritabanına güvenir. Geliştiricilerin uygulama kodu ile veritabanı arasında etkileşim kurması gerekir: veri okuma (sorgulama), yeni veri ekleme, mevcut veriyi güncelleme ve silme (CRUD işlemleri). Geleneksel olarak bu işlemler, SQL (Structured Query Language) sorgularını manuel olarak yazmayı, ADO.NET gibi alt seviye kütüphaneler kullanarak veritabanı bağlantıları açmayı, komutları çalıştırmayı ve dönen sonuçları uygulama içindeki nesne modellerine dönüştürmeyi içerirdi. Bu yaklaşım işlevsel olsa da, birkaç önemli zorluğu beraberinde getirir: Tekrarlayan Kod (Boilerplate): Bağlantı açma/kapama, komut oluşturma, parametre ekleme, veri okuyucu (DataReader) döngüleri gibi işlemler sıklıkla tekrarlanır. Nesne-İlişkisel Empedans Uyuşmazlığı (Object-Relational Impedance Mismatch): Uygulama kodu genellikle nesne yönelimli (sınıflar, özellikler, ilişkiler) iken, ilişkisel veritabanları tablolar, sütunlar ve yabancı anahtarlar üzerine kuruludur. Bu iki farklı dünyayı birbirine eşlemek (mapping) karmaşık ve hataya açık olabilir. SQL Bağımlılığı: Uygulama mantığı SQL sorgularıyla sıkı sıkıya bağlı hale gelir, bu da veritabanı şeması değişikliklerinde veya farklı bir veritabanı sistemine geçişte kodda önemli değişiklikler gerektirebilir. Tip Güvenliği Eksikliği: SQL sorguları genellikle metin (string) olarak yazılır, bu da derleme zamanı kontrolünü zorlaştırır ve çalışma zamanı hatalarına yol açabilir (örneğin, sütun adı yazım hataları). İşte bu zorlukları aşmak için Object-Relational Mapper (ORM) araçları geliştirilmiştir. ORM’ler, geliştiricilerin veritabanı tablolarını uygulama içindeki nesneler (entity’ler) olarak görmesini sağlar ve veritabanı işlemlerini (CRUD, sorgulama) doğrudan bu nesneler üzerinden, genellikle SQL yazmadan, tip güvenli bir şekilde yapmalarına olanak tanır. ORM, arka planda gerekli SQL sorgularını otomatik olarak üretir ve veritabanından gelen sonuçları nesnelere dönüştürür. Entity Framework Core (EF Core), Microsoft’un modern .NET platformu (.NET Core / .NET 5+) için geliştirdiği, açık kaynaklı, hafif, genişletilebilir ve çapraz platform destekli ORM çözümüdür. Geleneksel Entity Framework 6.x’in (sadece .NET Framework üzerinde çalışan) yeniden yazılmış ve modernleştirilmiş halidir. EF Core, geliştiricilerin tercih ettikleri .NET dilini (C#, F#) kullanarak veritabanlarıyla etkileşim kurmalarını sağlar ve veritabanına özgü kod yazma ihtiyacını büyük ölçüde azaltır. Bu makalede, EF Core’un temel kavramlarını, mimarisini, nasıl kurulup yapılandırılacağını, varlık (entity) sınıflarının nasıl tanımlanacağını, veritabanı bağlamını (DbContext), temel CRUD işlemlerini, güçlü LINQ sorgulama yeteneklerini, veritabanı geçişlerini (migrations) ve EF Core kullanırken dikkat edilmesi gereken önemli noktaları derinlemesine inceleyeceğiz. EF Core’un temellerini anlayarak, .NET uygulamalarınızda veri erişim katmanını daha verimli, sürdürülebilir ve modern bir şekilde nasıl oluşturabileceğinizi öğreneceksiniz. Bölüm 1: EF Core Temel Kavramları ve Mimarisi EF Core’u etkili bir şekilde kullanmak için temelindeki kavramları ve bileşenleri anlamak önemlidir. 1.1. ORM Olarak EF Core’un Görevi EF Core’un temel amacı, geliştiriciyi doğrudan SQL ve ADO.NET detaylarından soyutlayarak veritabanı işlemlerini basitleştirmektir. Bunu şu şekilde yapar: Eşleme (Mapping): Uygulama içindeki .NET sınıflarını (entity’ler) veritabanı tablolarıyla, sınıf özelliklerini (properties) tablo sütunlarıyla ve sınıflar arasındaki ilişkileri (navigation properties) veritabanı ilişkileriyle (foreign keys) eşler. Sorgu Üretimi: Geliştiricinin LINQ (Language-Integrated Query) kullanarak yazdığı tip güvenli sorguları analiz eder ve hedef veritabanının anlayacağı SQL sorgularına çevirir. Sonuç Dönüşümü: Veritabanından dönen sonuçları (genellikle tablo satırları) otomatik olarak ilgili .NET nesnelerine (entity örnekleri) dönüştürür. Değişiklik Takibi (Change Tracking): Bellekteki entity nesneleri üzerinde yapılan değişiklikleri (ekleme, güncelleme, silme) takip eder. Kalıcılık (Persistence): Takip edilen değişiklikleri SaveChanges metodu çağrıldığında uygun INSERT, UPDATE, DELETE SQL komutlarına dönüştürerek veritabanına uygular. 1.2. Temel Bileşenler Entity (Varlık): Veritabanındaki bir tabloya karşılık gelen basit bir .NET sınıfıdır (genellikle POCO — Plain Old CLR Object). Tablodaki sütunlar, sınıfın özellikleri (properties) ile temsil edilir. public class Product { public int ProductId { get; set; } // Primary Key (Convention) public string Name { get; set; } public decimal Price { get; set; } public int CategoryId { get; set; } // Foreign Key (Convention) // Navigation Property (İlişkili tabloya erişim için) public Category Category { get; set; } } public class Category { public int CategoryId { get; se
![C# ve .NET [23]](https://media2.dev.to/dynamic/image/width%3D1000,height%3D500,fit%3Dcover,gravity%3Dauto,format%3Dauto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsft7bav9xcqy46zctsm6.png)
Entity Framework Core Temelleri: .NET’te Veritabanı Etkileşimini Yeniden Şekillendirmek
Giriş: Veritabanı Erişiminin Evrimi ve ORM İhtiyacı
Modern uygulamaların büyük çoğunluğu, verileri kalıcı olarak saklamak ve yönetmek için bir veritabanına güvenir. Geliştiricilerin uygulama kodu ile veritabanı arasında etkileşim kurması gerekir: veri okuma (sorgulama), yeni veri ekleme, mevcut veriyi güncelleme ve silme (CRUD işlemleri). Geleneksel olarak bu işlemler, SQL (Structured Query Language) sorgularını manuel olarak yazmayı, ADO.NET gibi alt seviye kütüphaneler kullanarak veritabanı bağlantıları açmayı, komutları çalıştırmayı ve dönen sonuçları uygulama içindeki nesne modellerine dönüştürmeyi içerirdi. Bu yaklaşım işlevsel olsa da, birkaç önemli zorluğu beraberinde getirir:
Tekrarlayan Kod (Boilerplate): Bağlantı açma/kapama, komut oluşturma, parametre ekleme, veri okuyucu (DataReader) döngüleri gibi işlemler sıklıkla tekrarlanır.
Nesne-İlişkisel Empedans Uyuşmazlığı (Object-Relational Impedance Mismatch): Uygulama kodu genellikle nesne yönelimli (sınıflar, özellikler, ilişkiler) iken, ilişkisel veritabanları tablolar, sütunlar ve yabancı anahtarlar üzerine kuruludur. Bu iki farklı dünyayı birbirine eşlemek (mapping) karmaşık ve hataya açık olabilir.
SQL Bağımlılığı: Uygulama mantığı SQL sorgularıyla sıkı sıkıya bağlı hale gelir, bu da veritabanı şeması değişikliklerinde veya farklı bir veritabanı sistemine geçişte kodda önemli değişiklikler gerektirebilir.
Tip Güvenliği Eksikliği: SQL sorguları genellikle metin (string) olarak yazılır, bu da derleme zamanı kontrolünü zorlaştırır ve çalışma zamanı hatalarına yol açabilir (örneğin, sütun adı yazım hataları).
İşte bu zorlukları aşmak için Object-Relational Mapper (ORM) araçları geliştirilmiştir. ORM’ler, geliştiricilerin veritabanı tablolarını uygulama içindeki nesneler (entity’ler) olarak görmesini sağlar ve veritabanı işlemlerini (CRUD, sorgulama) doğrudan bu nesneler üzerinden, genellikle SQL yazmadan, tip güvenli bir şekilde yapmalarına olanak tanır. ORM, arka planda gerekli SQL sorgularını otomatik olarak üretir ve veritabanından gelen sonuçları nesnelere dönüştürür.
Entity Framework Core (EF Core), Microsoft’un modern .NET platformu (.NET Core / .NET 5+) için geliştirdiği, açık kaynaklı, hafif, genişletilebilir ve çapraz platform destekli ORM çözümüdür. Geleneksel Entity Framework 6.x’in (sadece .NET Framework üzerinde çalışan) yeniden yazılmış ve modernleştirilmiş halidir. EF Core, geliştiricilerin tercih ettikleri .NET dilini (C#, F#) kullanarak veritabanlarıyla etkileşim kurmalarını sağlar ve veritabanına özgü kod yazma ihtiyacını büyük ölçüde azaltır.
Bu makalede, EF Core’un temel kavramlarını, mimarisini, nasıl kurulup yapılandırılacağını, varlık (entity) sınıflarının nasıl tanımlanacağını, veritabanı bağlamını (DbContext), temel CRUD işlemlerini, güçlü LINQ sorgulama yeteneklerini, veritabanı geçişlerini (migrations) ve EF Core kullanırken dikkat edilmesi gereken önemli noktaları derinlemesine inceleyeceğiz. EF Core’un temellerini anlayarak, .NET uygulamalarınızda veri erişim katmanını daha verimli, sürdürülebilir ve modern bir şekilde nasıl oluşturabileceğinizi öğreneceksiniz.
Bölüm 1: EF Core Temel Kavramları ve Mimarisi
EF Core’u etkili bir şekilde kullanmak için temelindeki kavramları ve bileşenleri anlamak önemlidir.
1.1. ORM Olarak EF Core’un Görevi
EF Core’un temel amacı, geliştiriciyi doğrudan SQL ve ADO.NET detaylarından soyutlayarak veritabanı işlemlerini basitleştirmektir. Bunu şu şekilde yapar:
Eşleme (Mapping): Uygulama içindeki .NET sınıflarını (entity’ler) veritabanı tablolarıyla, sınıf özelliklerini (properties) tablo sütunlarıyla ve sınıflar arasındaki ilişkileri (navigation properties) veritabanı ilişkileriyle (foreign keys) eşler.
Sorgu Üretimi: Geliştiricinin LINQ (Language-Integrated Query) kullanarak yazdığı tip güvenli sorguları analiz eder ve hedef veritabanının anlayacağı SQL sorgularına çevirir.
Sonuç Dönüşümü: Veritabanından dönen sonuçları (genellikle tablo satırları) otomatik olarak ilgili .NET nesnelerine (entity örnekleri) dönüştürür.
Değişiklik Takibi (Change Tracking): Bellekteki entity nesneleri üzerinde yapılan değişiklikleri (ekleme, güncelleme, silme) takip eder.
Kalıcılık (Persistence): Takip edilen değişiklikleri SaveChanges metodu çağrıldığında uygun INSERT, UPDATE, DELETE SQL komutlarına dönüştürerek veritabanına uygular.
1.2. Temel Bileşenler
Entity (Varlık): Veritabanındaki bir tabloya karşılık gelen basit bir .NET sınıfıdır (genellikle POCO — Plain Old CLR Object). Tablodaki sütunlar, sınıfın özellikleri (properties) ile temsil edilir.
public class Product { public int ProductId { get; set; } // Primary Key (Convention) public string Name { get; set; } public decimal Price { get; set; } public int CategoryId { get; set; } // Foreign Key (Convention) // Navigation Property (İlişkili tabloya erişim için) public Category Category { get; set; } } public class Category { public int CategoryId { get; set; } // Primary Key public string Name { get; set; } // Collection Navigation Property (Birden çok ürüne erişim) public List Products { get; set; } = new List(); }
DbContext (Veritabanı Bağlamı): Veritabanı ile etkileşim kurulan ana sınıftır. Microsoft.EntityFrameworkCore.DbContext sınıfından türer. Görevleri:
Veritabanı bağlantısını yönetir.
Entity sınıflarını veritabanı tablolarıyla eşler (DbSet özellikleri aracılığıyla).
Entity’ler üzerindeki değişiklikleri takip eder.
LINQ sorgularını veritabanına gönderir.
Değişiklikleri veritabanına kaydeder (SaveChanges).
using Microsoft.EntityFrameworkCore; public class ApplicationDbContext : DbContext { // Yapıcı (DbContextOptions ile yapılandırma alır) public ApplicationDbContext(DbContextOptions options) : base(options) { } // Veritabanındaki tablolara karşılık gelen DbSet'ler public DbSet Products { get; set; } public DbSet Categories { get; set; } // Eşleme (Mapping) ve diğer yapılandırmalar için OnModelCreating (isteğe bağlı) protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Burada Fluent API ile detaylı yapılandırmalar yapılabilir // modelBuilder.Entity().ToTable("Urunler"); // modelBuilder.Entity().Property(p => p.Name).IsRequired().HasMaxLength(100); } }
DbSet: Belirli bir entity türü için veritabanındaki tabloyu temsil eder. LINQ sorgularını bu nesneler üzerinden yaparız ve CRUD işlemleri için metotlar içerir (Add, Update, Remove).
Veritabanı Sağlayıcısı (Database Provider): EF Core’un belirli bir veritabanı sistemiyle (SQL Server, PostgreSQL, SQLite, MySQL, Cosmos DB vb.) nasıl iletişim kuracağını bilen kütüphanedir. Her veritabanı için ayrı bir sağlayıcı NuGet paketi bulunur (örn. Microsoft.EntityFrameworkCore.SqlServer, Npgsql.EntityFrameworkCore.PostgreSQL). Sağlayıcı, LINQ sorgularını o veritabanına özgü SQL’e çevirir ve veritabanı bağlantısını yönetir.
Eşleme Kuralları (Mapping Conventions) ve Fluent API: EF Core, entity sınıflarını ve ilişkilerini veritabanıyla eşlemek için bir dizi kural (convention) kullanır (örn. Id veya ClassNameId isimli özellikler primary key olarak kabul edilir). Bu kurallar yeterli olmadığında veya özelleştirilmesi gerektiğinde, DbContext içindeki OnModelCreating metodu override edilerek Fluent API kullanılır. Fluent API, eşlemeleri, ilişkileri, kısıtlamaları (constraints), indeksleri vb. kod aracılığıyla detaylı bir şekilde yapılandırmayı sağlar. Alternatif olarak Data Annotations (entity sınıfları üzerine eklenen [Table], [Key], [Required], [MaxLength] gibi öznitelikler) da kullanılabilir, ancak Fluent API daha fazla kontrol sunar ve entity sınıflarını temiz tutar.
Bölüm 2: EF Core Kurulum ve Yapılandırma
Bir .NET projesinde EF Core kullanmaya başlamak için birkaç adım gereklidir:
2.1. Gerekli NuGet Paketlerini Yükleme
Veritabanı Sağlayıcısı Paketi: Hedef veritabanınıza uygun sağlayıcıyı yükleyin.
SQL Server: Microsoft.EntityFrameworkCore.SqlServer
PostgreSQL: Npgsql.EntityFrameworkCore.PostgreSQL
SQLite: Microsoft.EntityFrameworkCore.Sqlite
MySQL: Pomelo.EntityFrameworkCore.MySql veya MySql.EntityFrameworkCore
In-Memory (Test için): Microsoft.EntityFrameworkCore.InMemory
EF Core Araçları Paketi (Opsiyonel ama Şiddetle Tavsiye Edilir): Veritabanı geçişleri (migrations) ve tersine mühendislik (scaffolding) gibi komutları çalıştırmak için gereklidir.
Microsoft.EntityFrameworkCore.Tools: Paket Yöneticisi Konsolu (PMC — Visual Studio’da) veya .NET CLI için komutları içerir (dotnet ef …).
Paketleri yüklemek için NuGet Paket Yöneticisi (Visual Studio’da) veya dotnet CLI kullanılabilir:
Örnek: SQL Server sağlayıcısı ve araçları yükleme
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
2.2. Entity Sınıflarını Tanımlama
Veritabanı tablolarınıza karşılık gelecek POCO sınıflarını oluşturun (yukarıdaki Product ve Category örneği gibi).
2.3. DbContext Sınıfını Oluşturma
DbContext’ten türeyen kendi bağlam sınıfınızı oluşturun (ApplicationDbContext örneği gibi) ve DbSet özelliklerini ekleyin.
2.4. DbContext’i Yapılandırma ve Kaydetme (DI)
DbContext’in hangi veritabanı sağlayıcısını ve hangi bağlantı dizesini (connection string) kullanacağını bilmesi gerekir. Bu yapılandırma genellikle Program.cs (veya eski Startup.cs) içinde yapılır ve DbContext DI konteynerine kaydedilir.
Örnek (Program.cs — ASP.NET Core):
using Microsoft.EntityFrameworkCore;
// ... diğer using'ler ...
var builder = WebApplication.CreateBuilder(args);
// 1. Bağlantı Dizesini Al (genellikle appsettings.json'dan)
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// 2. DbContext'i DI Konteynerine Kaydet ve Sağlayıcıyı Yapılandır
builder.Services.AddDbContext(options =>
options.UseSqlServer(connectionString)); // SQL Server sağlayıcısını kullan
// ASP.NET Core servislerini ekle (örneğin)
builder.Services.AddControllersWithViews();
var app = builder.Build();
// ... Middleware yapılandırması ...
app.Run();
GetConnectionString(“DefaultConnection”): appsettings.json içindeki ConnectionStrings bölümünden “DefaultConnection” adlı bağlantı dizesini okur.
// appsettings.json { "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=MyEfCoreDb;Trusted_Connection=True;" }, // ... diğer ayarlar ... }
AddDbContext: ApplicationDbContext’i DI konteynerine kaydeder. Varsayılan olarak Scoped yaşam döngüsüyle kaydedilir (her HTTP isteği için bir DbContext örneği).
options.UseSqlServer(connectionString): EF Core’a SQL Server sağlayıcısını ve ilgili bağlantı dizesini kullanmasını söyler. Farklı sağlayıcılar için farklı Use… metotları kullanılır (UseNpgsql, UseSqlite vb.).
Bölüm 3: CRUD İşlemleri (Create, Read, Update, Delete)
DbContext ve DbSet üzerinden temel veritabanı işlemleri kolayca gerçekleştirilir.
3.1. Veri Ekleme (Create)
Yeni bir entity nesnesi örneği oluşturun ve özelliklerini ayarlayın.
DbSet.Add(entity) metodunu kullanarak nesneyi DbContext’in takip etmesini sağlayın. Bu aşamada veritabanına henüz bir şey yazılmaz, sadece nesne “Eklendi” (Added) durumuna geçer.
DbContext.SaveChanges() veya SaveChangesAsync() metodunu çağırarak takip edilen tüm değişiklikleri (bu durumda INSERT komutunu) veritabanına gönderin. SaveChanges işlemi atomiktir; ya tüm değişiklikler başarılı olur ya da hiçbiri olmaz (genellikle bir transaction içinde çalışır).
// DbContext örneği DI ile enjekte edilir
private readonly ApplicationDbContext _context;
public async Task AddNewCategoryAsync(string categoryName)
{
var newCategory = new Category { Name = categoryName };
_context.Categories.Add(newCategory); // Değişiklik takibine ekle (Added state)
try
{
await _context.SaveChangesAsync(); // Veritabanına INSERT komutunu gönder
// SaveChangesAsync sonrası newCategory.CategoryId otomatik olarak atanır (eğer DB üretiyorsa)
Console.WriteLine($"Kategori eklendi. ID: {newCategory.CategoryId}");
}
catch (DbUpdateException ex)
{
// Hata yönetimi (örn. unique constraint ihlali)
Console.WriteLine($"Ekleme hatası: {ex.InnerException?.Message ?? ex.Message}");
}
}
3.2. Veri Okuma (Read / Sorgulama)
Veri okuma işlemleri genellikle LINQ kullanılarak yapılır (Bölüm 4'te detaylı). DbSet doğrudan LINQ sorgularını destekler.
Tüm Kayıtları Alma: _context.Products.ToListAsync()
Belirli Bir Kaydı Alma (Primary Key ile): _context.Products.FindAsync(productId) (Önce bellekte arar, yoksa DB’ye gider) veya _context.Products.FirstOrDefaultAsync(p => p.ProductId == productId)
Filtreleme: _context.Products.Where(p => p.Price > 100).ToListAsync()
Sıralama: _context.Products.OrderBy(p => p.Name).ToListAsync()
public async Task GetProductByIdAsync(int productId)
{
// FindAsync primary key ile arama için optimize edilmiştir
var product = await _context.Products.FindAsync(productId);
return product; // Bulunamazsa null döner
}
public async Task> GetProductsByNameAsync(string nameFilter)
{
// Where ile filtreleme
var products = await _context.Products
.Where(p => p.Name.Contains(nameFilter))
.OrderBy(p => p.Name) // İsim sırasına göre
.ToListAsync(); // Sonuçları liste olarak al
return products;
}
3.3. Veri Güncelleme (Update)
EF Core’da güncelleme yapmanın birkaç yolu vardır:
Bağlı (Connected) Senaryo (En Yaygın):
Güncellenecek entity’yi veritabanından okuyun (FindAsync, FirstOrDefaultAsync vb.). Bu işlem entity’nin DbContext tarafından takip edilmesini sağlar.
Entity’nin özelliklerini değiştirin.
DbContext.SaveChangesAsync() metodunu çağırın. EF Core, değişiklik takip mekanizması sayesinde hangi özelliklerin değiştiğini anlar ve sadece değişen sütunlar için optimize bir UPDATE komutu üretir.
public async Task UpdateProductPriceAsync(int productId, decimal newPrice) { var product = await _context.Products.FindAsync(productId); if (product == null) { return false; // Ürün bulunamadı } product.Price = newPrice; // Takip edilen entity'nin özelliğini değiştir // Başka bir değişiklik yapmaya gerek yok, SaveChanges değişikliği algılar await _context.SaveChangesAsync(); // Veritabanına UPDATE komutunu gönder return true; }
Bağlantısız (Disconnected) Senaryo: Entity veritabanından okunup DbContext kapatıldıktan sonra (veya farklı bir DbContext ile) değiştirildiyse ve sonra güncellenmek isteniyorsa:
Güncellenmiş entity nesnesini alın (örneğin, bir web isteğinden).
DbSet.Update(entity) metodunu çağırarak entity’yi “Değiştirildi” (Modified) durumuna getirin. Bu durumda EF Core, hangi özelliklerin gerçekten değiştiğini bilemez ve tüm eşlenmiş sütunları UPDATE komutuna dahil eder.
DbContext.SaveChangesAsync() çağırın.
public async Task UpdateProductDisconnectedAsync(Product updatedProduct) { // updatedProduct'ın başka bir yerden (örn. API isteği) geldiğini varsayalım _context.Products.Update(updatedProduct); // Tüm alanları güncellenecek olarak işaretle try { await _context.SaveChangesAsync(); return true; } catch (DbUpdateConcurrencyException) // Başka bir işlem aynı anda silmiş/güncellemiş olabilir { // Eşzamanlılık çakışması yönetimi return false; }
Bağlantısız senaryoda sadece belirli alanları güncellemek için daha gelişmiş teknikler (örn. Attach ve Entry(…).Property(…).IsModified = true) kullanılabilir.
3.4. Veri Silme (Delete)
Silinecek entity’yi veritabanından okuyun (takip edilmesi için).
DbSet.Remove(entity) metodunu çağırarak entity’yi “Silindi” (Deleted) durumuna getirin.
DbContext.SaveChangesAsync() çağırarak veritabanına DELETE komutunu gönderin.
public async Task DeleteCategoryAsync(int categoryId)
{
var category = await _context.Categories.FindAsync(categoryId);
if (category == null)
{
return false; // Kategori bulunamadı
}
// İlişkili ürünler varsa ne olacağı veritabanı kısıtlamalarına bağlıdır
// (Cascade delete, restrict vb.) EF Core yapılandırmasıyla yönetilebilir.
_context.Categories.Remove(category); // Silindi olarak işaretle
await _context.SaveChangesAsync(); // Veritabanına DELETE komutunu gönder
return true;
}
Bölüm 4: LINQ ile Sorgulama
EF Core’un en güçlü yanlarından biri, veritabanı sorgularını C# içinde LINQ (Language-Integrated Query) kullanarak yazabilmektir. Bu, tip güvenli, okunabilir ve derleme zamanında kontrol edilen sorgular oluşturmayı sağlar. EF Core, LINQ sorgularınızı analiz eder ve veritabanı sağlayıcısı aracılığıyla uygun SQL’e çevirir.
Yaygın LINQ Operatörleri:
Filtreleme: Where(p => p.Price > 100 && p.Category.Name == “Elektronik”)
Sıralama: OrderBy(p => p.Price), OrderByDescending(p => p.CreationDate), ThenBy(p => p.Name)
Projeksiyon (Seçme): Select(p => p.Name), Select(p => new { p.Name, p.Price }) (anonim tip), Select(p => new ProductDto { ProductName = p.Name, UnitPrice = p.Price }) (DTO’ya dönüştürme — Önemli! Sadece gerekli sütunları çekmek için kullanılır).
Sayfalama: Skip(10).Take(5) (ilk 10'u atla, sonraki 5'i al)
Gruplama: GroupBy(p => p.CategoryId)
Birleştirme (Join): Join(…) (genellikle navigation property’ler kullanıldığı için explicit Join’e nadiren ihtiyaç duyulur)
Toplama (Aggregation): Count(), Sum(p => p.Price), Average(p => p.Stock), Max(), Min()
Varlık Kontrolü: Any(p => p.Stock < 5), All(p => p.IsActive)
Tek Kayıt Alma: First(), FirstOrDefault(), Single(), SingleOrDefault() (Koşula uyan ilk/tek kaydı alır, bulamazsa veya birden fazla bulursa farklı davranışlar sergilerler).
İlişkili Verileri Yükleme (Loading Related Data):
Eager Loading (Hevesli Yükleme): Sorgu sırasında ilişkili verileri Include(p => p.Category) veya ThenInclude(c => c.Supplier) gibi metotlarla ana sorguya dahil eder. Tek bir veritabanı sorgusu yapar (genellikle JOIN ile), ancak çok fazla veri çekebilir.
var productsWithCategories = await _context.Products .Include(p => p.Category) // Category bilgisini de yükle .Where(p => p.Price > 50) .ToListAsync(); // productsWithCategories listesindeki her ürünün Category özelliği dolu gelir.
Explicit Loading (Açık Yükleme): Ana entity yüklendikten sonra, ihtiyaç duyulduğunda ilişkili verileri _context.Entry(product).Reference(p => p.Category).LoadAsync() veya _context.Entry(category).Collection(c => c.Products).LoadAsync() gibi metotlarla ayrı bir sorguyla yükler.
Lazy Loading (Tembel Yükleme): İlişkili bir navigation property’ye ilk kez erişildiğinde otomatik olarak veritabanından yüklenir. Kurulumu kolaydır (Microsoft.EntityFrameworkCore.Proxies paketi ve virtual navigation property’ler gerektirir) ancak performansı olumsuz etkileyebilir (N+1 sorgu problemi) ve dikkatli kullanılmalıdır. Genellikle Eager veya Explicit Loading tercih edilir.
LINQ Sorgularının Çalışması:
LINQ sorguları yazıldığında hemen çalışmazlar (deferred execution). Sorgu ancak .ToList(), .ToArray(), .FirstOrDefault(), foreach döngüsü gibi bir terminal operatörü çağrıldığında veya sonuçlar üzerinde işlem yapıldığında veritabanına gönderilir ve çalıştırılır. Bu, sorguları parçalar halinde oluşturup birleştirmeyi mümkün kılar.
Bölüm 5: Veritabanı Geçişleri (Migrations)
Uygulama geliştirme sürecinde, entity modelleriniz (dolayısıyla veritabanı şemanız) zamanla değişebilir (yeni tablolar, sütunlar eklenir, ilişkiler değişir vb.). Bu değişiklikleri veritabanına güvenli ve tekrarlanabilir bir şekilde uygulamak için EF Core Migrations özelliğini sunar.
Migrations Nasıl Çalışır?
Model Değişikliği: Entity sınıflarınızda veya OnModelCreating içinde değişiklik yaparsınız.
Geçiş Oluşturma (Add Migration): dotnet ef migrations add MigrationName (CLI) veya Add-Migration MigrationName (PMC) komutunu çalıştırırsınız. EF Core, mevcut veritabanı modeli (son geçişten alınan veya hiç yoksa boş) ile mevcut kod modeliniz arasındaki farkları algılar ve bu farkları uygulayacak C# kodunu (Up metodu) ve geri alacak C# kodunu (Down metodu) içeren bir geçiş dosyası oluşturur. Projenizde bir Migrations klasörü oluşturulur.
Veritabanını Güncelleme (Update Database): dotnet ef database update (CLI) veya Update-Database (PMC) komutunu çalıştırırsınız. EF Core, veritabanında özel bir __EFMigrationsHistory tablosuna bakarak henüz uygulanmamış geçişleri bulur ve bunları sırayla çalıştırarak veritabanı şemasını günceller.
Avantajları:
Veritabanı şeması değişikliklerini kaynak kontrol sisteminde (Git vb.) yönetilebilir hale getirir.
Farklı ortamlardaki (geliştirme, test, üretim) veritabanlarını tutarlı bir şekilde güncellemeyi sağlar.
Şema değişikliklerini otomatik veya manuel olarak uygulama esnekliği sunar.
Takım çalışmasını kolaylaştırır.
Bölüm 6: İleri Düzey Konular ve En İyi Pratikler
Eşzamanlılık Kontrolü (Concurrency Control): Birden fazla kullanıcının aynı anda aynı veriyi güncellemeye çalışması durumunda oluşabilecek çakışmaları yönetmek için mekanizmalar sunar (örn. [Timestamp] veya [ConcurrencyCheck] öznitelikleri, IsConcurrencyToken() Fluent API’si). Çakışma olduğunda DbUpdateConcurrencyException fırlatılır.
İşlemler (Transactions): SaveChanges varsayılan olarak bir transaction içinde çalışır. Ancak birden fazla SaveChanges çağrısını veya DbContext dışındaki işlemleri tek bir atomik birimde toplamak için DbContext.Database.BeginTransactionAsync() ile manuel transaction yönetimi yapılabilir.
Ham SQL Sorguları (Raw SQL Queries): LINQ’nun yetersiz kaldığı veya çok karmaşık SQL gerektiği durumlarda DbSet.FromSqlRaw(“…”) veya DbContext.Database.ExecuteSqlRawAsync(“…”) gibi metotlarla ham SQL sorguları çalıştırmak mümkündür. Ancak SQL enjeksiyon riskine karşı dikkatli olunmalı ve parametreler güvenli bir şekilde kullanılmalıdır (FromSqlInterpolated).
Bağlantı Esnekliği (Connection Resiliency): Özellikle bulut ortamlarında geçici ağ hatalarına karşı otomatik yeniden deneme mekanizmaları yapılandırılabilir (EnableRetryOnFailure).
Performans Optimizasyonu:
Sadece Gerekli Veriyi Çekin: Select ile projeksiyon kullanarak sadece ihtiyaç duyulan sütunları getirin.
Değişiklik Takibini Kapatma: Sadece veri okuyup üzerinde değişiklik yapmayacaksanız, AsNoTracking() metodunu kullanarak değişiklik takibini kapatmak performansı artırır.
Doğru Yükleme Stratejisini Seçin: Eager Loading (Include) veya Explicit Loading kullanarak N+1 sorgu probleminden kaçının. Lazy Loading’i dikkatli kullanın.
Sorguları Optimize Edin: LINQ sorgularının veritabanında verimli çalışacak şekilde yazılmasına dikkat edin (örneğin, filtrelemeyi mümkün olduğunca erken yapın). Üretilen SQL’i incelemek için loglamayı kullanın.
İndeksleme: Performansı artırmak için sık sorgulanan sütunlara veritabanı indeksleri ekleyin (Migrations veya Fluent API ile tanımlanabilir).
Toplu İşlemler (Bulk Operations): Çok sayıda kaydı tek seferde eklemek/güncellemek/silmek için EFCore.BulkExtensions gibi üçüncü parti kütüphaneler performansı ciddi şekilde artırabilir (EF Core’un kendisi henüz tam gelişmiş bulk desteği sunmuyor).
DbContext Yaşam Döngüsü: ASP.NET Core’da DbContext genellikle Scoped olarak kaydedilir (istek başına bir tane). Uzun süreli işlemlerde veya farklı senaryolarda (örn. masaüstü uygulamaları) DbContext’in yaşam döngüsünü doğru yönetmek önemlidir. DbContext hafif bir nesnedir ve genellikle kısa ömürlü olması amaçlanır.
Test Etme: Veritabanı etkileşimlerini test etmek için In-Memory veritabanı sağlayıcısı (Microsoft.EntityFrameworkCore.InMemory) veya test için gerçek bir veritabanı (örn. SQLite veya Testcontainers ile geçici bir veritabanı) kullanılabilir. Bağımlılıkları soyutlamak için Repository Pattern gibi desenler de kullanılabilir.
Sonuç: EF Core ile Verimli Veritabanı Erişimi
Entity Framework Core, .NET geliştiricileri için modern, güçlü ve esnek bir ORM çözümüdür. Veritabanı etkileşimlerini nesne yönelimli bir yaklaşımla basitleştirerek, tip güvenliği sağlayarak ve tekrarlayan kodları azaltarak geliştirme sürecini önemli ölçüde hızlandırır. LINQ entegrasyonu sayesinde karmaşık sorgular okunabilir ve sürdürülebilir bir şekilde yazılabilir. Migrations özelliği ile veritabanı şeması evrimi güvenli bir şekilde yönetilebilir.
EF Core’un temel kavramlarını (DbContext, DbSet, Entity, Provider), CRUD işlemlerini, LINQ sorgulama yeteneklerini, ilişkili veri yükleme stratejilerini ve Migrations kullanımını anlamak, .NET ile veri odaklı uygulamalar geliştirmek için kritik öneme sahiptir. Performans optimizasyonu, eşzamanlılık kontrolü ve doğru test stratejileri gibi ileri düzey konulara da hakim olmak, EF Core’un tüm potansiyelinden faydalanmanızı sağlar.
Açık kaynaklı yapısı, çapraz platform desteği ve sürekli gelişimi ile EF Core, .NET ekosisteminin vazgeçilmez bir parçasıdır ve modern .NET uygulamalarında veri erişim katmanını oluşturmak için standart haline gelmiştir.
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin