Unit Of Work Design Pattern Nedir? Nasıl Kullanılır?
Merhabalar,
Enterprise uygulamalarda sıkça kullanılan(özellikle repository pattern ile) UnitofWork design pattern’den bahsedeceğim.
UnitofWork Design Pattern Nedir?
Bu pattern, iş katmanında yapılan her değişikliğin anlık olarak database e yansıması yerine, işlemlerin toplu halde tek bir kanaldan gerçekleşmesini sağlar.
Neden UnitofWork?
UnitofWork’ün avantajı nedir? diyecek olursanız şöyle ki;
Bir e-ticaret sitesini düşünelim.Bir ürün satın alma aşamasında birden fazla aşama vardır.Sepetteki ürünlerinize göre ekstra ürün eklemek isteyeceğiniz bir sayfa, sonra kart bilgilerini gireceğiniz bir sayfa, en son alışverişi tamamlayacağınız bir sayfa olur.Bu aşamaların her birinde database e kayıt atmak yerine, en son satın alma aşamasında bu girilen kayıtların toplu halde database e aktarımı hem performans, hemde sağlıklı data açısından önemli olacaktır.Çünkü son aşamaya gelindiğinde satın almadan vazgeçilmesi halinde önceki aşamalarda oluşan datalar dummy data olarak database’de ekstra yer işgal edecektir.
Bu ve bunun gibi, birden fazla işlemi tek bir işlem olarak düşünmemiz gereken yerlerde bu design pattern’i kullanabiliriz.
İşlemler tek bir kanaldan(tek bir transaction) toplu halde yapıldığı için performansı artı yönde etkileyecektir.Ayrıca işlemleri geri alma(rollback), hangi tabloda ne işlem yapıldı kaç kayıt eklendi gibi sorulara da cevap verebilir.
Nasıl Kullanılır?
UnitofWork genelde repository pattern ile uygulanır.Örneğimiz de bu iki pattern i kullanacak şekilde olacak.
Database olarak Northwind database ini kullanacağım.Category tablosu için tek metottan oluşan bir repository yazacağım.
public class CategoryRepository : ICategoryRepository{private readonly Northwind _context;public CategoryRepository(Northwind context){_context = context;}public void AddCategory(Category entity){_context.Entry(entity).State = EntityState.Added;}}
Daha sonra UnitofWork class ını oluşturalım.
public class UnitofWork{private readonly Northwind _context;private CategoryRepository categoryRepository;public UnitofWork(){this._context = new Northwind();}public CategoryRepository CategoryRepository => this.categoryRepository ?? (this.categoryRepository = new CategoryRepository(_context));public void Save(){try{using (var transaction = _context.Database.BeginTransaction()){try{_context.SaveChanges();transaction.Commit();}catch{transaction.Rollback();throw;}}}catch (Exception e){//TODO:Logging}}private bool disposed = false;protected virtual void Dispose(bool disposing){if (!this.disposed){if (disposing){_context.Dispose();}}this.disposed = true;}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}}
Görüldüğü üzere Save() metodunda önce ilgili context te bir transaction başlatılıyor.Daha sonra context in savechanges() metodu çağırılıyor yalnız bu aşamada henüz database de işlem yapılmış değil.Transaction’ın commit() metodu çağırıldıktan sonra database e ilgili değişiklikler yansımış oluyor.Eğer herhangi bir hata alınırsa catch bloğunda rollback işlemi yapılıyor ve database de herhangi bir değişiklik olmuyor.
UnitofWork unitofWork = new UnitofWork();
unitofWork.CategoryRepository.Add(_category);
unitofWork.Save();
Tabi bu örnek amaçlı basit bir işlemdi.Birden fazla database işlemi yaptığınız bir kod bloğunu düşünürseniz, veya yine birden fazla işlem yapan bir store procedure çalıştırıldığında herhangi bir hata alındığında rollback işlemi daha bir önemli hale gelecektir.
Özetlemek gerekirse UnitofWork özellikle Generic Repository Pattern ile çok uyumlu bir şekilde kullanılabilen faydalı bir patterndir.Tabi design patternler ihtiyaca göre kullanılır.Yoğun veri işleminin olduğu uygulamalarda(özellikle e-ticaret siteleri) sıklıkça kullanılmaktadır.
Umuyorum faydalı bir makale olmuştur.Herkese iyi kodlamalar.