I have implemented a generic repository pattern in my project. My folder structure is as shown below:
Abstractions Folder:
Contains generic interfaces like IReadRepository and IWriteRepository, along with model-specific interfaces (e.g., ICategoryRepository, ITutorialRepository, etc.) where I can define model-specific methods if needed. Concretes Folder:
Contains the generic repository implementations like ReadRepository and WriteRepository. Additionally, I create specific repositories for each model (e.g., CategoryRepository, TutorialRepository), which inherit the generic repositories but also allow me to add model-specific methods. My Approach: Generic Implementation: The ReadRepository and WriteRepository classes handle basic CRUD operations for all models. Model-Specific Repositories: If a model requires specific methods or logic, I add them in the respective repository (e.g., CategoryRepository).
this is my interface
public interface IReadRepository<T> : IRepository<T> where T :
BaseEntity, new()
{
Task<IEnumerable<T>> GetAllAsync();
Task<T?> GetByIdAsync(int id, bool isTracking, params string[] includes);
Task<T?> GetSingleByConditionAsync(Expression<Func<T, bool>> condition);
IQueryable<T> GetAllByConditionAsync(Expression<Func<T, bool>> condition);
Task CreateAsync(T entity);
void Update(T entity);
void Delete(T entity);
void DeleteRange(params T[] entities);
Task<int> SaveChangeAsync();
}
and this repository
public class WriteRepository<T> : IWriteRepository<T> where T :
BaseEntity, new()
{
private readonly AppDbContext _context;
public WriteRepository(AppDbContext context)
{
_context = context;
}
public DbSet<T> Table => _context.Set<T>();
public async Task CreateAsync(T entity)
{
await Table.AddAsync(entity);
}
public void Delete(T entity)
{
Table.Remove(entity);
}
public void DeleteRange(params T[] entities)
{
Table.RemoveRange(entities);
}
public async Task<int> SaveChangeAsync()
{
int rows = await _context.SaveChangesAsync();
return rows;
}
public void Update(T entity)
{
Table.Update(entity);
}
public async Task<T?> GetByIdAsync(int id, bool isTracking, params string[] includes)
{
IQueryable<T> query = Table.AsQueryable();
if (!isTracking)
{
query = query.AsNoTracking();
}
if (includes.Length > 0)
{
foreach (string include in includes)
{
query = query.Include(include);
}
}
T? entity = await query.SingleOrDefaultAsync(e => e.Id == id);
return entity;
}
public async Task<T?> GetSingleByConditionAsync(Expression<Func<T, bool>> condition)
{
IQueryable<T> query = Table.AsQueryable();
T? entity = await query.SingleOrDefaultAsync(condition);
return entity;
}
public IQueryable<T> GetAllByConditionAsync(Expression<Func<T, bool>> condition)
{
IQueryable<T> query = Table.AsQueryable();
query = query.Where(condition);
return query;
}
public async Task<IEnumerable<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
}