TEMEL
DbContext & DbSet Kurulumu
DbContext, uygulama ile veritabanı arasındaki köprüdür. Her sorgu, her kayıt bu köprüden geçer.
Temel Kurulum
// NuGet: dotnet add package Microsoft.EntityFrameworkCore.SqlServer
// appsettings.json
{
"ConnectionStrings": {
"Default": "Server=.;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;"
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
Connection String parametreleri (SQL Server):
| Parametre | Varsayılan | Açıklama |
|---|---|---|
Server |
— | Sunucu adı (. = localhost, .\SQLEXPRESS) |
Database |
— | Veritabanı adı |
Trusted_Connection |
false | True → Windows Auth |
User Id / Password |
— | SQL Auth (Trusted_Connection=False ise) |
TrustServerCertificate |
false | Dev'de True (self-signed cert) |
MultipleActiveResultSets |
false | Nested reader'lar için (EF Core'da genelde gereksiz) |
Encrypt |
true (.NET 7+) | Bağlantı şifreleme |
Connection Timeout |
15 | Bağlantı zaman aşımı (sn) |
Max Pool Size |
100 | Connection pool boyutu |
// NuGet: dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
// NuGet: dotnet add package EFCore.NamingConventions (opsiyonel, şiddetle önerilen)
// appsettings.json
{
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=mydb;Username=app;Password=***;"
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default"))
.UseSnakeCaseNamingConvention()); // Products → products, OrderDate → order_date
Connection String parametreleri (Npgsql):
| Parametre | Varsayılan | Açıklama |
|---|---|---|
Host |
localhost | Sunucu adresi |
Port |
5432 | PostgreSQL portu |
Database |
— | Veritabanı adı |
Username |
— | Kullanıcı |
Password |
— | Şifre |
SSL Mode |
Prefer | Require → production'da zorunlu |
Maximum Pool Size |
100 | İç havuz boyutu |
Connection Idle Lifetime |
300 | Boş bağlantı ömrü (sn) |
Include Error Detail |
false | Dev'de true yap (detaylı hata) |
Command Timeout |
30 | Sorgu zaman aşımı (sn) |
Naming Convention:
PostgreSQL dünyasında standartsnake_case'dir.UseSnakeCaseNamingConvention()olmadan EF Core, C#'takiPascalCaseisimleri doğrudan kullanır →"Products"(tırnaklı). Bu,psql'de her seferinde çift tırnak yazmak zorunda bırakır.
🚨 Güvenlik — Connection String Yönetimi:
Yukarıdaki örneklerde şifreler doğrudanappsettings.jsoniçinde gösterilmiştir — bu sadece geliştirme ortamı içindir. Production'da connection string'leri asla kaynak kodda veya config dosyasında saklamayın:
- Development:
dotnet user-secrets set "ConnectionStrings:Default" "..."- Production: Environment variable, Azure Key Vault veya AWS Secrets Manager kullanın
appsettings.Development.jsondosyasını.gitignore'a ekleyin
DbContext Sınıfı
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Tüm config sınıflarını assembly'den otomatik yükle
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
DbSet<Product>= SQL'dekiProductstablosu.context.Products.Where(...)yazıldığında arka plandaSELECT ... FROM Products WHERE ...oluşur.
Lifetime:
AddDbContextvarsayılan olarak Scoped kayıt yapar — her HTTP request'te bir instance oluşur ve request sonunda dispose edilir.
DbContext Yaşam Döngüsü & IDbContextFactory
DbContext'in ne zaman oluşup ne zaman yok edildiğini anlamak, memory leak ve thread-safety hatalarından korunmanın temelidir. AddDbContext varsayılan olarak Scoped kayıt yapar — her HTTP request'te bir instance oluşur, request bitince dispose edilir.
Yaşam Döngüsü
Kayıt Yöntemleri
| Yöntem | Ne zaman? | Nasıl? |
|---|---|---|
AddDbContext |
Controller, API endpoint | Her request'te otomatik oluşur/yok olur |
AddDbContextFactory |
Background Service, Blazor Server, gRPC | factory.CreateDbContextAsync() ile manuel oluşturursun |
AddPooledDbContextFactory |
Yüksek throughput | Context nesnesi havuzlanır, allocation azalır |
// Scoped (varsayılan — Controller/API için)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
// Factory (Background Service / Blazor Server için)
builder.Services.AddDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(connectionString));
// Pooled Factory (yüksek throughput için)
builder.Services.AddPooledDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(connectionString));
// Scoped (varsayılan — Controller/API için)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(connectionString)
.UseSnakeCaseNamingConvention());
// Factory (Background Service / Blazor Server için)
builder.Services.AddDbContextFactory<AppDbContext>(options =>
options.UseNpgsql(connectionString)
.UseSnakeCaseNamingConvention());
// Pooled Factory (yüksek throughput için)
builder.Services.AddPooledDbContextFactory<AppDbContext>(options =>
options.UseNpgsql(connectionString)
.UseSnakeCaseNamingConvention());
Npgsql'in
Maximum Pool Size→ TCP bağlantılarını havuzlar. EF Core'un Pool'u → DbContext nesnesini havuzlar. İkisi birlikte çalışır.
IDbContextFactory Kullanımı
Request dışı senaryolarda (Background Service, Blazor Server, gRPC) context'i kendin oluşturup kendin dispose etmelisin:
public class OrderProcessingService : BackgroundService
{
private readonly IDbContextFactory<AppDbContext> _factory;
public OrderProcessingService(IDbContextFactory<AppDbContext> factory)
=> _factory = factory;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await using var context = await _factory.CreateDbContextAsync(stoppingToken);
var pending = await context.Orders
.Where(o => o.Status == OrderStatus.Pending)
.ToListAsync(stoppingToken);
foreach (var order in pending)
order.Status = OrderStatus.Processing;
await context.SaveChangesAsync(stoppingToken);
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
}
Dikkat Edilmesi Gerekenler
// ❌ DbContext'i Singleton olarak kaydetme — thread-safe DEĞİL
builder.Services.AddSingleton<AppDbContext>();
// ❌ Aynı DbContext ile paralel sorgu — ÇÖKER
var task1 = context.Products.ToListAsync();
var task2 = context.Orders.ToListAsync();
await Task.WhenAll(task1, task2); // DbContext thread-safe değil!
// ✅ Paralel iş gerekiyorsa her biri için ayrı context
await using var ctx1 = await factory.CreateDbContextAsync();
await using var ctx2 = await factory.CreateDbContextAsync();
await Task.WhenAll(ctx1.Products.ToListAsync(), ctx2.Orders.ToListAsync());