الـ Generic Repository هو نموذج Architecture يعتمد على Structural Pattern (مستوى تصميم) لتحقيق Separation of Concerns في تطوير البرامج.

الهدف من Generic Repository هو تحديد وتنظيم الوصول إلى البيانات دون التعرض لتفاصيلImplementation (تنفيذ) الخاصة بالبيانات. يسمح هذا النموذج بتحسين قابلية الصيانة والتنويع في تطبيق البرامج، وكمان يتيح تحسين قابلية النمو والتنمية.

الـ Generic Repository هو طبقة وسطى بين تطبيق البرنامج (Client) وبيانات التطبيق (Data Storage). يتحمل Generic Repository مسؤولية تحديد كيفية الوصول إلى البيانات (مثل: قراءة، كتابة، تحديث، حذف) دون أن يتعرض التطبيق للتفاصيلImplementation (تنفيذ) الخاصة بالبيانات.

مثلا، إنك تريد أن تحصل على قائمة من جميع الأشياء في قاعدة البيانات. بناء على Generic Repository، يمكنك تحديد طريقة الوصول إلى هذه القائمة دون أن تعلم تفاصيلImplementation (تنفيذ) الخاصة بالبيانات، مثل:

  • إنك تريد أن تحصل على قائمة من جميع الأشياء في قاعدة البيانات.
  • إنك تريد أن تحصل على قائمة من جميع الأشياء التي تم إضافتها في آخر 24 ساعة.
  • إنك تريد أن تحصل على قائمة من جميع الأشياء التي تمت إزالتها في آخر 24 ساعة.

الـGeneric Repository يسمح لك بتحديد هذه الطرق دون أن تعلم تفاصيل Implementation (تنفيذ) الخاصة بالبيانات، مما يتيح تحسين قابلية الصيانة والتنويع في تطبيق البرامج.

فيما يلي مثال بسيط لتنفيذ Generic Repository في ASP.NET Core:

// Interface
public interface IGenericRepository<T>
    where T : class
{
    IEnumerable<T> GetAll();
    T GetById(int id);
    void Create(T entity);
    void Update(T entity);
    void Delete(int id);
}
 
// Class
public class GenericRepository<T> : IGenericRepository<T>
    where T : class
{
    private readonly DbContext _context;
 
    public GenericRepository(DbContext context)
    {
        _context = context;
    }
 
    public IEnumerable<T> GetAll()
    {
        return _context.Set<T>().ToList();
    }
 
    public T GetById(int id)
    {
        return _context.Set<T>().Find(id);
    }
 
    public void Create(T entity)
    {
        _context.Set<T>().Add(entity);
        _context.SaveChanges();
    }
 
    public void Update(T entity)
    {
        _context.Set<T>().Update(entity);
        _context.SaveChanges();
    }
 
    public void Delete(int id)
    {
        var entity = GetById(id);
        if (entity!= null)
        {
            _context.Set<T>().Remove(entity);
            _context.SaveChanges();
        }
    }
}

في هذا المثال، GenericRepository<T> هو Implementation (تنفيذ) للinterface IGenericRepository<T>. يحتوي على طريقة GetAll() للتحصل على قائمة من جميع الأشياء في قاعدة البيانات، وطريقة GetById() للتحصل على شئ واحد بناء على معرفه. كما يحتوي على طريقة Create() للتحصل على شئ جديد، وطريقة Update() لتحديث شئ موجود، وطريقة Delete() لتحذف شئ موجود.

إنك تستطيع الآن استخدام GenericRepository<T> في تطبيقك دون أن تعلم تفاصيلImplementation (تنفيذ) الخاصة بالبيانات.

In Our Project

احنا هنعمل Product repository بس المفروض قبل يبقا عندي Generic Repository

Interface

هنحتاج نعمل Interface اسمه IGenericRepository وزي ما اتفقنا الـ Interfaces بتتحط في الـ Core Layer فأنا هعمل فولدر وممكن أسميه Repositories أو Repositories.Contract ودا الأحسن وهعمل فولدر كمان لبعد كدا اسمه Services.Contract

namspace Talabat.Core.Repositories.Contract;
public interface IGenericRepository<T> where T : BaseEntity
{
	// We will make now: 2 methods
	Task<T?> GetAsync(int id);
	Task<IEnumerable<T>> GetAllAsync();
}
  • لازم تبقا BaseEntity
  • المفروض بنعمل 5 Signatures لل 5 methods بتاعنا بس احنا هنعمل اتنين بس get, get all لأن مش طبيعي الشخص اللي بيتسوق يعدل أو يضيف أو يحذف

الاتنين دول هيبقوا شغالين Synchronoss فهنستخدم Task وهترجع T أو IEnumerable من الـ T Cs Enumerable

Class

بعدين نروح في الـ Repository ونضيف الـ Class بتاعنا

public class GenericRepository<T> : IGenericRepository where T : BaseEntity
{
	private readonly StoreContext _dbContext;
	public GenericRepository(StroreContext dbContext)
	{
		_dbContext = dbContext;
	}
	public Task<IEnumerable<T>> GetAllAsync()
	{
		return await _dbContext.Set<T>().ToListAsync();
	}
	public Task<T?> GetAsync(int id)
	{
		return await _dbContext.Set<T>().FindAsync(id);
	}
}
  • لازم برضو نحدد انه الـ T لازم تبقا BaseEntity
  • محتاج أستخدم الـ DbContext عشان ترجعلي الداتا دي
  • ممكن يبقا عندي Repository تانية وهيبقا فيها Generic Repository تانية وعنده DbContext تانية
  • لازم تخلي اللي بتجيب بالـ Id تقبل Nullable لأنها ممكن ترجع Null
  • مش هنعمل Product ولا Category ولا Brand ك Repository لأني مش هضيف فيهم أي حاجة جديدة انما لو هزود فانكشن ولا حاجة لازم نعمل واحد ويورث من دا