?? Master the Repository Pattern in .NET – Clean, Flexible & Scalable!
Paresh Bhayani
?? Empowering Start-ups through Technologies | .NET | C# | SQL | JavaScript | Full Stack Developer
CRUD operations are the heart of software development, but handling them efficiently can be challenging. That’s where the Repository Pattern comes in!
It introduces a layer of abstraction between the business logic and data access, making your code cleaner, more maintainable, and easier to test.
?? Why Use the Repository Pattern?
? Abstraction – Separates business logic from database logic for better modularity. ? Testability – Enables unit testing by mocking repositories. ? Flexibility – Centralizes data access logic, promoting code reusability.
?? How Does It Work?
Let's break it down with a simple example:
?? 1?? Define a Repository Interface
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(int id);
}
?? Defines CRUD operations for any entity.
?? 2?? Implement the Generic Repository
public class Repository<T> : IRepository<T> where T : class
{
private readonly AppDbContext _context;
private readonly DbSet<T> _dbSet;
public Repository(AppDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
public async Task AddAsync(T entity)
{
await _dbSet.AddAsync(entity);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(T entity)
{
_dbSet.Update(entity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var entity = await _dbSet.FindAsync(id);
if (entity != null)
{
_dbSet.Remove(entity);
await _context.SaveChangesAsync();
}
}
}
?? Encapsulates CRUD operations for any entity.
?? Prevents code duplication.
public class ProductService
{
private readonly IRepository<Product> _repository;
public ProductService(IRepository<Product> repository)
{
_repository = repository;
}
public async Task<IEnumerable<Product>> GetProductsAsync()
{
return await _repository.GetAllAsync();
}
}
?? Keeps business logic separate from database logic.
? Advanced Features of the Repository Pattern
?? Flexible Querying with Func<IQueryable<T>>
public async Task<IEnumerable<T>> GetFilteredAsync(Func<IQueryable<T>, IQueryable<T>> filter)
{
var query = filter(_dbSet);
return await query.ToListAsync();
}
?? Apply dynamic filtering & sorting at runtime!
?? Efficient Pagination
public async Task<(IEnumerable<T>, int)> GetPagedAsync(int page, int pageSize)
{
var totalItems = await _dbSet.CountAsync();
var items = await _dbSet.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
return (items, totalItems);
}
?? Reduces load by fetching data in chunks.
?? Using Cancellation Tokens for Performance
public async Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken)
{
return await _dbSet.ToListAsync(cancellationToken);
}
?? Improves responsiveness by allowing operation cancellations.
?? Common Misconception
Many think the Repository Pattern makes switching databases easy. While it helps with abstraction, database migration (e.g., SQL → NoSQL) usually requires significant changes in query logic.
?? Start Using the Repository Pattern Today!
? Cleaner & more maintainable code
? Easier unit testing
? Better separation of concerns
#RepositoryPattern #DotNetCore #CSharp #EntityFramework #CleanArchitecture #BestPractices #CodeOptimization #DesignPatterns #UnitTesting ??