Entity Framework Core, .NET ekosisteminin standart ORM (Object-Relational Mapping) kütüphanesidir. Code-First yaklaşım ile C# sınıflarınızdan veritabanı şeması oluşturulur; migration'lar sayesinde şema değişiklikleri versiyon kontrolüyle yönetilir.
Code-First ile Model Tanımlama
// CSHARP //
// Models/Makale.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BlogApi.Models;
public class Makale
{
public int Id { get; set; }
[Required, MaxLength(300)]
public string Baslik { get; set; } = string.Empty;
[Required, MaxLength(350)]
public string Slug { get; set; } = string.Empty;
[Required]
public string Icerik { get; set; } = string.Empty;
public bool Yayinlandi { get; set; } = false;
public int Gorunumler { get; set; } = 0;
public DateTime OlusturulduAt { get; set; } = DateTime.UtcNow;
// Foreign key
public int YazarId { get; set; }
// Navigation properties
[ForeignKey(nameof(YazarId))]
public Kullanici Yazar { get; set; } = null!;
public ICollection<Etiket> Etiketler { get; set; } = [];
public ICollection<Yorum> Yorumlar { get; set; } = [];
}
public class Kullanici
{
public int Id { get; set; }
[Required, MaxLength(100)]
public string Ad { get; set; } = string.Empty;
[Required, MaxLength(255)]
public string Email { get; set; } = string.Empty;
[Required]
public string SifreHash { get; set; } = string.Empty;
public string Rol { get; set; } = "okuyucu";
public DateTime OlusturulduAt { get; set; } = DateTime.UtcNow;
public ICollection<Makale> Makaleler { get; set; } = [];
}DbContext Yapılandırması
// CSHARP //
// Data/AppDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace BlogApi.Data;
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<Kullanici> Kullanicilar => Set<Kullanici>();
public DbSet<Makale> Makaleler => Set<Makale>();
public DbSet<Etiket> Etiketler => Set<Etiket>();
public DbSet<Yorum> Yorumlar => Set<Yorum>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Unique constraint
modelBuilder.Entity<Kullanici>()
.HasIndex(k => k.Email)
.IsUnique();
modelBuilder.Entity<Makale>()
.HasIndex(m => m.Slug)
.IsUnique();
// Many-to-many: Makale <-> Etiket
modelBuilder.Entity<Makale>()
.HasMany(m => m.Etiketler)
.WithMany(e => e.Makaleler)
.UsingEntity("MakaleEtiketler");
// Soft delete filter
modelBuilder.Entity<Makale>()
.HasQueryFilter(m => !m.Silindi);
// Tablo adlarını özelleştir
modelBuilder.Entity<Kullanici>().ToTable("kullanicilar");
modelBuilder.Entity<Makale>().ToTable("makaleler");
// Seed data
modelBuilder.Entity<Kullanici>().HasData(
new Kullanici { Id = 1, Ad = "Admin", Email = "admin@blog.com",
SifreHash = "$2b$12$...", Rol = "admin" }
);
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default"),
o => o.EnableRetryOnFailure(maxRetryCount: 3)));Migration Yönetimi
// CSHARP //
// Yeni migration — özelleştirme
public partial class GorunumlerSutunuEkle : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Gorunumler",
table: "makaleler",
nullable: false,
defaultValue: 0);
// Var olan verileri güncelle
migrationBuilder.Sql(
"UPDATE makaleler SET Gorunumler = 0 WHERE Gorunumler IS NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(name: "Gorunumler", table: "makaleler");
}
}LINQ ile Sorgulama
// CSHARP //
// Repository pattern
public class MakaleRepository(AppDbContext db) : IMakaleRepository
{
// Temel sorgu
public async Task<List<Makale>> ListeleAsync(int sayfa, int boyut)
{
return await db.Makaleler
.Include(m => m.Yazar)
.Include(m => m.Etiketler)
.Where(m => m.Yayinlandi)
.OrderByDescending(m => m.OlusturulduAt)
.Skip((sayfa - 1) * boyut)
.Take(boyut)
.AsNoTracking() // Okuma için tracking gerekmez
.ToListAsync();
}
// Projection ile sadece gerekli alanlar
public async Task<List<MakaleOnizleme>> OnizlemeleriGetirAsync()
{
return await db.Makaleler
.Where(m => m.Yayinlandi)
.Select(m => new MakaleOnizleme(
m.Id,
m.Baslik,
m.Slug,
m.Icerik.Substring(0, Math.Min(160, m.Icerik.Length)),
m.OlusturulduAt,
m.Yazar.Ad
))
.OrderByDescending(m => m.OlusturulduAt)
.ToListAsync();
}
// İlişkili veri ile birlikte getir
public async Task<Makale?> SlugIleGetirAsync(string slug)
{
return await db.Makaleler
.Include(m => m.Yazar)
.Include(m => m.Etiketler)
.Include(m => m.Yorumlar.OrderBy(y => y.OlusturulduAt))
.ThenInclude(y => y.Yazar)
.FirstOrDefaultAsync(m => m.Slug == slug && m.Yayinlandi);
}
// Aggregate sorgular
public async Task<Dictionary<string, int>> EtiketIstatistikleriAsync()
{
return await db.Etiketler
.Select(e => new { e.Isim, MakaleSayisi = e.Makaleler.Count(m => m.Yayinlandi) })
.OrderByDescending(x => x.MakaleSayisi)
.ToDictionaryAsync(x => x.Isim, x => x.MakaleSayisi);
}
}Transaction ve Toplu İşlemler
// CSHARP //
// Transaction kullanımı
public async Task<Makale> SiparisTamamlaAsync(MakaleOlusturDto dto, int yazarId)
{
await using var transaction = await db.Database.BeginTransactionAsync();
try
{
var makale = new Makale
{
Baslik = dto.Baslik,
Slug = SlugOlustur(dto.Baslik),
Icerik = dto.Icerik,
YazarId = yazarId,
};
db.Makaleler.Add(makale);
await db.SaveChangesAsync();
// Etiketleri ekle
foreach (var etiketIsim in dto.Etiketler)
{
var etiket = await db.Etiketler.FirstOrDefaultAsync(e => e.Isim == etiketIsim)
?? db.Etiketler.Add(new Etiket { Isim = etiketIsim }).Entity;
makale.Etiketler.Add(etiket);
}
await db.SaveChangesAsync();
await transaction.CommitAsync();
return makale;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
// EF Core 7+ ExecuteUpdate / ExecuteDelete — toplu işlem
await db.Makaleler
.Where(m => m.OlusturulduAt < DateTime.UtcNow.AddYears(-2) && !m.Yayinlandi)
.ExecuteDeleteAsync(); // Toplu silme — tek SQL, object tracking yok
await db.Makaleler
.Where(m => m.YazarId == yazarId)
.ExecuteUpdateAsync(s => s.SetProperty(m => m.Gorunumler, 0));Sonuç
Entity Framework Core ile Code-First modelleme, migration yönetimi ve LINQ sorguları .NET backend geliştirmenin temel araçlarıdır. AsNoTracking, projection ve toplu işlemler ile performanslı sorgular yazabilirsiniz. Bir sonraki derste ASP.NET Core ile SignalR kullanarak gerçek zamanlı bildirimler geliştireceğiz.