Yükleniyor

ASP.NET Core’da Unit of Work kullanımı

Blog Kategorileri

.Net Core Mimariler
Tasarım Desenleri
ORM Araçları
API Geliştirme
Web Geliştirme
Veritabanları
22 Aralık 2025 Pazartesi
ASP.NET Core’da Unit of Work kullanımı

Merhaba,

Kurumsal uygulamalarda veri erişim katmanının yönetilebilir, test edilebilir ve sürdürülebilir olması kritik öneme sahiptir. Bu noktada Unit of Work (UoW) Pattern, özellikle Entity Framework Core kullanılan projelerde sıkça tercih edilen bir tasarım desenidir. Bu yazıda, .NET Core 10 üzerinde  Unit of Work kullanımını, basit ve gerçekçi bir örnek üzerinden ele alacağım.

Unit of Work Pattern Nedir?

Unit of Work, bir iş akışı boyunca yapılan tüm veritabanı işlemlerini tek bir işlem altında toplar ve bu işlemlerin tek noktadan commit veya rollback edilmesini sağlar.

Başlıca faydaları şunlardır:

  1. Transaction yönetimini merkezileştirir

  2. Repository katmanları arasında tutarlılık sağlar

  3. DbContext.SaveChanges() çağrılarının dağınık kullanılmasını engeller

  4. Test edilebilirliği artırır

  5. Clean Architecture ve DDD yaklaşımlarıyla uyumludur

IUnitOfWork Arayüzü

Öncelikle, uygulama genelinde kullanılacak olan IUnitOfWork arayüzünü tanımlayalım

public interface IUnitOfWork
{
   void SaveChanges();
}

Bu arayüz:

  1. İşlemlerin veritabanına yansıtılmasını tek bir metod üzerinden yönetmemizi sağlayacak

UnitOfWork Implementasyonu

Ardından, bu arayüzü AppDbContext üzerinden implemente eden sınıfı inceleyelim:

public class UnitOfWork(AppDbContext _context) : IUnitOfWork
{
    public void SaveChanges()
    {
        _context.SaveChanges();
    }
}

Bu yapı:

  1. SaveChanges() çağrısını tek bir noktada toplar

Bu yaklaşım, servis veya controller katmanında doğrudan DbContext kullanımını engeller.

Repository Pattern ile Birlikte Kullanım

Unit of Work genellikle Repository Pattern ile birlikte kullanılır. Örnek bir senaryoyu ele alalım.

public interface IProductRepository
{
    void Add(Product product);
}
public class ProductRepository(AppDbContext context) : IProductRepository
{
    public void Add(Product product)
    {
        context.Products.Add(product);
    }
}

Bu noktada repository yalnızca veriyi işaretler, commit işlemi yapmaz. (veri tabanına yansımaz)

Service Katmanında IUnitOfWork Kullanımı

public class ProductService(IProductRepository productRepository, IUnitOfWork unitOfWork)
{
    public void CreateProduct(Product product)
    {
        productRepository.Add(product);
        unitOfWork.SaveChanges();
    }
}

Bu yapı sayesinde:

  1. Birden fazla repository işlemi tek transaction altında toplanabilir

  2. İş kuralları servis katmanında kalır

  3. Veri erişim detayları soyutlanmış olur

Dependency Injection Konfigürasyonu

builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();

Scoped yaşam döngüsü sayesinde:

  1. Her HTTP request için tek bir DbContext

  2. Dolayısıyla tek bir Unit of Work instance’ı kullanılır

Neden Bu Yaklaşımı Tercih Etmeliyiz?

IUnitOfWork kullanımı şu problemlerin önüne geçer:

  1. Her repository’de SaveChanges() çağrılması

  2. Transaction yönetiminin kontrolsüz olması

  3. Test yazımında DbContext bağımlılığı

  4. Kod tekrarları

Özellikle büyüyen projelerde, bu pattern uzun vadede ciddi bakım avantajı sağlar.

Gerçek Dünya Örneği

E-Ticaret Senaryosu

Bir sipariş; sipariş + sipariş kalemleri + ödeme birlikte oluşmuyorsa, hiçbiri oluşmamalıdır.

Senaryodaki Tablolar

  1. Orders

  2. OrderItems

  3. Payments

public interface IOrderRepository
{
    void Add(Order order);
}
public class OrderRepository(AppDbContext context) : IOrderRepository
{
    public void Add(Order order)
    {
        context.Orders.Add(order);
    }
}

public interface IPaymentRepository
{
    void Add(Payment payment);
}
public class PaymentRepository(AppDbContext context) : IPaymentRepository
{
    public void Add(Payment payment)
    {
        context.Payments.Add(payment);
    }
}

public interface IOrderService
{
    void PlaceOrder();
}
public class OrderService(IOrderRepository orderRepository,  IPaymentRepository paymentRepository,  IUnitOfWork unitOfWork) : IOrderService
{
    public  void PlaceOrder()
    {
        var order = new Order  //sipariş bilgisi oluşturulur
        {
            CustomerId = Guid.NewGuid(),
            CreatedAt = DateTime.UtcNow
        };

        order.Items.Add(new OrderItem  //sipariş içeriği tutulur
        {
            ProductId = Guid.NewGuid(),
            Quantity = 2,
            UnitPrice = 100
        });

        orderRepository.Add(order); 

        var payment = new Payment   // ödeme bilgileri tutulur
        {
            OrderId = order.Id,
            Amount = 200,
            PaidAt = DateTime.UtcNow
        };

        paymentRepository.Add(payment);

        unitOfWork.SaveChanges();   // tüm işlemler tek bir yerden commit edilir
    }
}

Böylece  üç işleminde yönetimi sağlanır eğer birisinde bir hata olursa hiçbiri kaydolmaz Order kaydoldu OrderItem kaydoldu  Payment kaydolmadı gibi bir işlem söz konusu olamaz, veri tutarlılığı sağlanır.

Olasi bir hata durumunda

  1. Exception fırlar

  2. SaveChanges() çalışmaz

  3. Veritabanına tek kayıt bile yazılmaz

Yanlış Kullanım

orderRepository.Add(order);
unitOfWork.SaveChanges();

paymentRepository.Add(payment);
unitOfWork.SaveChanges();

Her bir işaretten sonra unitOfWork.SaveChanges(); cağırılırsa o işlem kendi içinde değerlendirilmiş olur yukarıdaki senaryoda;

Order Eklendi,

Ödeme bilgisi alındı ama veritabanına yansıtırken anlık bir hata oldu. Sipariş bilgisi eklendi ödeme bilgisi eklenemedi ve veri tutarsızlığı olmuş oldu.

Sonuç

.NET Core 10 ile birlikte gelen modern C# özellikleri ve EF Core altyapısı, Unit of Work Pattern’in uygulanmasını hem sade hem de güçlü hale getiriyor. Verdiğimiz IUnitOfWork örneği, küçük projeler için yeterli olduğu gibi, kurumsal mimarilere de kolayca evrilebilir.

Diğer bloglarımda görüşmek üzere 👋