Unit Of Work ile Generic Repository Tasarım Deseni

Unit of Work kalıbını veritabanı ile yapılacak bütün işlemleri tek bir kanaldan yürütme ve bir yerde hafızada tutmayı amaçlar. Bu sebeple katmanlı mimaride DAL(Data Acces Layer) içerisinde yer alır. Unit of Work yapılan işlemlerin bir birim üzerinden ele alıp, bu olayları saklayan daha sonra ise toplu halde kaydedilmesi, bir hata ile karşılaşıldığında geri alınması ve ya iptal edilmesini sağlamaktır.

Unit of Work tasarım kalıbı genellikle repository tasarım kalıbıyla birlikte kullanılmaktadır. Ancak bu şart değildir. Elinizde CRUD işlemlerinin yapıldığı sınıflar olması da yeterlidir. Ana mantık bir DBContext’i Repository sınıflarımıza vermektir.

Temel olarak ;

1. CRUD işlemlerinin yapıldığı sınıflara Unit Of Work içerisinden DbContext verilmeli.

2.CRUD işlemini yapan sınıf kaydetme işlemlerini DBContext ‘in SubmitChanges() metodunu çağırmamalı onun yerine işlemler Unit of Work sınıfına yazılmış Save() metodu çağırmalı. Yani tüm işlemleri Unit of workte yapacağımızdan save işlemi de unit of workte bulunmalı.

3.İşlemleri veri tabanına aktarma sırasında hata ile karşılaşıldığında Rollback işlemleri için metot yazılmalı.

Avantajları

1. Veri iletişimini tek kanaldan sağlamak.

2. Veritabanıyla alakalı bir problem meydana gelirse nereye bakmamız gerektiğini biliyor oluruz.

3. Kod tekrarının önüne geçip tekrar tekrar ihtiyaç duyulabilecek sorguları metoda bağlamak.

4. İleride entity frameworkten vazgeçip yerine bir ORM kullanacağımızı varsayalım tek dokunmamız gereken kısım DAL olacaktır.

Konuya bir örnek ile devam edelim… Örneğimizin üyeleri aşağıdaki gibi olsun

IRepository : Ortak olarak tanımlanacak repository metotlarını içerir. Get(id),GetAll(),Add(Entity),Remove(id)
Repository : İnterfacede tanımlanan metotları doldurur.
IDepartmentRepository : Eğer özel tanımlamamız gereken ekstra özel metotlar olursa kullanacağımız interface. IRepositoryden kalıtılmalı.
DepartmentRepository :  İnterfacede tanımlanan metotları doldurur.

IUnitOfWork ve UnitOfWork : tüm repositoryler toplanır ve saveChanges burada olur.

1.Modeller

 public class Department
 {
    public Department()
    {
     personels = new List<Personel>();
    }
     public int Id{get; set;}
     public int Name{get; set;} 

   public ICollection<Personel> personels{get; set;}
 }

 public class Personel
 {
     public int Id{get; set;}
     public int Name{get; set;}
     public string LastName{get; set;}
     public int DepartmentId{get; set;}

    public Department Department{get; set;}  
 }

public class PersonnelContext : DbContext
 {
     public PersonnelContext() : base("")
     { 
     }
      public DbSet<Department> Departments {get; set;}
      public DbSet<Personel> Personnels {get; set;}
 }

2.Repositoryler

public interface IRepository<TEntity> : class   //interface jeneriktir yani ne geleceğini bilemeyiz. Runtimeda belirlenir. Ama bildiğimiz tek şey TEntity kesinlikle bir class.
 {
     TEntity  GetById(int id);
     IEnumerable<TEntity>   GetAll();
      void  Add(TEntity entity);
      void AddRange(IEnumerable<TEntity> entities);
      void Remove(int id);
      void   RemoveRange(IEnumerable<TEntity> entities);
 }

class Repository<TEntity> : IRepository<TEntity> where TEntity : class //sana TEntity tipinde birşey gelecek ama ne geleceğini ben de bilmiyorum.
{
     protected DbContext _context;
     private DbSet<TEntity> _dbset;

     public Repository(DbContext context)  //repositoryi kullanmak isteyen buna bir defa dbcontext vermek zorunda
     {  _context = context; 
         _dbSet = _context.Set(); 

     }
   
      public void Add(TEntity entity)
      {
           //_context.Departments.Add(entity);
            //  _context.Set.Add(entity);
             _dbSet.Add(entity);
      }

     public void AddRange(IEnumerable<TEntity> entities)
     {
         // _context.Set.AddRange(entitites);
           _dbSet.AddRange(entities);
     }

     public IEnumerable<TEntity> GetAll()
     {
         return _dbSet.toList();
     }

     public TEntity GetById(int id)
     {
       return _dbSet.Find(id);
     }
    
     public void Remove(int id)
     {
      _dbSet.Remove(GetById(id));
     }

     public void RemoveRabge(IEnumerable<TEntity> entities)
     {
      _dbSet.RemoveRange(entities);
     }
}

Departmanlara özgü işlemlerin var ise

 public interface IDepartmentRepository : IRepository<Department//departmana özgü olduğundan tip gönderebildik.
   {
             IEnumerable<Department>  GetTopDepartments(int count);
             IEnumareble<Department>   GetDepartmentsWithPersonnels();
   }

  public class DepartmentRepository : Repository<Department> , IDepartmentRepository
  { 
       public DepartmnetRepository (PersonnelContext context) : base(context)
       {
       }

       public IEnumerable<Department> GetDepartmentsWithPersonels()
       {
        return PersonnelContext.Departments.Include("Personnels").ToList();
       }

       public IEnumerable<Department> GetTopDepartments(int count)
       {
         return PersonnelContext.Departments.Take(count);
       }

       public PersonnelContext PersonnelContext {get{return _context as PersonnelContext} } //bu cast işlemine sürekli ihtiyac duyacağız.
  }
  
   public interface IPersonnelRepository : IRepository<Personnel>
   {
     IEnumerable<Personnel> GetPersonnelsWithDepartments();
   }
  
    public class PersonnelRepository : Repository<Personnel> , IPersonnelRepository
    {
       public PersonnelRepository(PersonnelContext context) : base(context) //sen personelRepositoryi kullanmak istiyorsan önce personelContext vermek zorundasın. 
       {}   
    
       public IEnumerable<Personnel> GetPersonnelsWithDepartments()
       {
          return PersonnelContext.Personnels.Include("Department").ToList();
       }   
       public PersonnelContext PersonnelContext {get{return _context}} 
    }


 

3. Unit Of Work Kısmı : tüm repositoryler burada toplanır.

 public interface IUnitOfWork : IDisposable
 {
     IDepartmentRepository DepartmentRepository{get;}
     IPersonnelRepository  PersonelRepository{get;}

     int Complete();

 }

 public class UnitOfWork : IUnitOfWork
 {
       private PersonnelContext _personnelContext;
   
        public UnitOfWork(PersonnelContext context)
        {
          _personnelContext = context;
           DepartmentRepository = new DepartmentRepository(_personnelContext);
           PersonnelRepository  = new PersonnelRepository (_personnelContext);
        }

        public IDepartmentRepository DepartmentRepository {get; private set; }
        public IPersonnelRepository  PersonnelRepository {get; private set;}

        public int Complete()
        {
            return  _personnelContext.SaveChanges();
        }

       public void Dipsose()
       {
         _personnelContext.Dispose(); 
       }
 }

**************************************************************************

4. Console UI Kısmı

  static void Main(string[] args)
  {
        UnitOfWork unitOfWork = new UnitOfWork(new PersonnelContext());

        //tüm repositoryler tek bir classtan gelir.
        unitOfWork.DepartmentRepository --> hem ortak hem deparmana özgü metotlar
        unitOfWork.PersonnelRepository   --> aynı şekilde

        unitOfWork.DepartmentRepository.Add(new Department{.........}) --> veri tabanına değil rama ekleme
        unitOfWork.PazarlamaRepository.Add(new Department{.........}) --> veri tabanına değil rama ekleme
        unitOfWork.Complete();  ---> tamamlama .. iki kayıt tek transaction üzerinden eklendi.
  }