İLERİ
Global Exception Handling & Retry
Veritabanı hataları kaçınılmaz: deadlock, timeout, constraint violation, connection drop. Bu hataları doğru yakalamak (hangi exception = hangi hata), kullanıcıya anlamlı mesaj vermek ve geçici hatalar için retry mekanizması kurmak production stabilitesi için şart.
Veritabanı sağlayıcısı
Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.
EF Core Exception Tipleri
| Exception | Sebep | Çözüm |
|---|---|---|
DbUpdateException |
INSERT/UPDATE/DELETE hatası | Inner exception'a bak |
DbUpdateConcurrencyException |
Concurrency conflict (row version) | Retry veya merge |
RetryLimitExceededException |
Retry politikası tükendi | Alert + fallback |
SqlException (Number: 1205) |
Deadlock | Otomatik retry |
SqlException (Number: -2) |
Timeout | Retry veya timeout artır |
SqlException (Number: 2601/2627) |
Unique constraint violation | Kullanıcıya bilgi ver |
Constraint Violation Handling
try
{
context.Products.Add(new Product { Sku = "EXISTING-SKU" });
await context.SaveChangesAsync();
}
catch (DbUpdateException ex) when (ex.InnerException is SqlException sqlEx)
{
switch (sqlEx.Number)
{
case 2601: // Unique index violation
case 2627: // Unique constraint violation
throw new BusinessException($"Bu SKU zaten mevcut: {sqlEx.Message}");
case 547: // FK constraint violation
throw new BusinessException("İlişkili kayıt bulunamadı veya silinemez.");
default:
throw; // Bilinmeyen hata — yukarı fırlat
}
}
-- SqlException 2601 tetikleyen SQL:
INSERT INTO [Products] ([Sku], [Name]) VALUES ('EXISTING-SKU', 'Test');
-- Msg 2601: Cannot insert duplicate key row in object 'Products'
-- with unique index 'IX_Products_Sku'.
-- PostgresException 23505 tetikleyen SQL:
INSERT INTO products (sku, name) VALUES ('EXISTING-SKU', 'Test');
-- ERROR: duplicate key value violates unique constraint "ix_products_sku"
-- DETAIL: Key (sku)=(EXISTING-SKU) already exists.
Concurrency Exception Handling
async Task UpdateProductWithRetry(int productId, decimal newPrice, int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
var product = await context.Products.FindAsync(productId);
product!.Price = newPrice;
await context.SaveChangesAsync();
return; // Başarılı
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
// Strateji 1: "Database Wins" — DB'deki değeri al
await entry.ReloadAsync(); // DB'den tekrar oku
// Strateji 2: "Client Wins" — kendi değerini zorla yaz
// entry.OriginalValues.SetValues(await entry.GetDatabaseValuesAsync());
if (attempt == maxRetries - 1) throw;
}
}
}
Deadlock Retry
// Connection Resiliency ile otomatik deadlock retry
options.UseSqlServer(conn, o => o.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: new[] { 1205 })); // 1205 = Deadlock
// Manuel retry pattern (daha fazla kontrol)
public static class RetryHelper
{
public static async Task<T> ExecuteWithRetryAsync<T>(
Func<Task<T>> operation,
int maxRetries = 3,
int baseDelayMs = 100)
{
for (int i = 0; i < maxRetries; i++)
{
try { return await operation(); }
catch (Exception ex) when (IsTransient(ex) && i < maxRetries - 1)
{
// Exponential backoff
await Task.Delay(baseDelayMs * (int)Math.Pow(2, i));
}
}
return await operation(); // Son deneme — hata fırlatabilir
}
private static bool IsTransient(Exception ex)
=> ex is DbUpdateException { InnerException: SqlException sql }
&& sql.Number is 1205 or -2 or 40613 or 40197;
}
Timeout Yönetimi
// Global command timeout (saniye)
options.UseSqlServer(conn, o => o.CommandTimeout(60));
// Tek sorgu için timeout
context.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
// Uzun çalışan sorgu — CancellationToken ile iptal
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
var result = await context.Products
.Where(p => p.IsActive)
.ToListAsync(cts.Token);
}
catch (OperationCanceledException)
{
// Sorgu timeout'a uğradı
logger.LogWarning("Product query timed out after 30s");
}
Exception Türleri Karar Ağacı
DbUpdateException yakalandı
├── InnerException is SqlException?
│ ├── Number 2601/2627 → Unique violation → Kullanıcıya "zaten var" mesajı
│ ├── Number 547 → FK violation → "İlişkili kayıt problemi"
│ ├── Number 1205 → Deadlock → Retry (otomatik veya manuel)
│ ├── Number -2 → Timeout → Retry veya timeout artır
│ └── Diğer → Log + generic hata
├── Is DbUpdateConcurrencyException?
│ └── Concurrency conflict → Reload + retry veya merge UI
└── Diğer → Log + throw