๐ Step 2: Implementing a Generic Repository (Introducing Category Repository)
๐ก Goal:
In this article, we will refactor our Product Repository by implementing a Generic Repository Pattern. Additionally, we will introduce the Category Repository, demonstrating how the Generic Repository improves code reusability and maintainability.
๐ Updated Project Structure (With Generic Repository)
๐ ProductApp
โโโ ๐ Core # Domain Layer
โ โโโ ๐ Entities
โ โ โโโ BaseEntity.cs
โ โ โโโ Product.cs
โ โ โโโ Category.cs
โ โโโ ๐ Interfaces
โ โ โโโ IRepository.cs # Generic Repository Interface
โ โ โโโ IProductRepository.cs
โ โ โโโ ICategoryRepository.cs
โ
โโโ ๐ Infrastructure # Data Access Layer
โ โโโ ๐ Data
โ โ โโโ Repository.cs # Generic Repository Implementation
โ โ โโโ ProductRepository.cs
โ โ โโโ CategoryRepository.cs
โ โ โโโ StoreContext.cs
โ
โโโ ๐ API # Presentation Layer
โ โโโ ๐ Controllers
โ โ โโโ ProductsController.cs
โ โ โโโ CategoriesController.cs
โ โโโ ๐ DTOs
โ โ โโโ ProductDto.cs
โ โ โโโ CategoryDto.cs
1๏ธโฃ Define the Base Entity
๐ Core/Entities/BaseEntity.cs
namespace Core.Entities
{
public abstract class BaseEntity
{
public int Id { get; set; }
}
}
2๏ธโฃ Define the Generic Repository Interface
๐ Core/Interfaces/IRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Core.Interfaces
{
public interface IRepository<T> where T : BaseEntity
{
Task<T?> GetByIdAsync(int id);
Task<List<T>> ListAllAsync();
void Add(T entity);
void Update(T entity);
void Remove(T entity);
Task<bool> SaveAllAsync();
}
}
3๏ธโฃ Implement the Generic Repository
๐ Infrastructure/Data/Repository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Core.Entities;
using Core.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Data
{
public class Repository<T> : IRepository<T> where T : BaseEntity
{
private readonly StoreContext _context;
private readonly DbSet<T> _dbSet;
public Repository(StoreContext context)
{
_context = context;
_dbSet = _context.Set<T>();
}
public async Task<T?> GetByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public async Task<List<T>> ListAllAsync()
{
return await _dbSet.ToListAsync();
}
public void Add(T entity)
{
_dbSet.Add(entity);
}
public void Update(T entity)
{
_dbSet.Update(entity);
}
public void Remove(T entity)
{
_dbSet.Remove(entity);
}
public async Task<bool> SaveAllAsync()
{
return await _context.SaveChangesAsync() > 0;
}
}
}
4๏ธโฃ Define the **Category
**** Entity**
๐ Core/Entities/Category.cs
namespace Core.Entities
{
public class Category : BaseEntity
{
public string Name { get; set; } = string.Empty;
}
}
5๏ธโฃ Implement the **ICategoryRepository
**** Interface**
๐ Core/Interfaces/ICategoryRepository.cs
using Core.Entities;
namespace Core.Interfaces
{
public interface ICategoryRepository : IRepository<Category>
{
}
}
6๏ธโฃ Implement the **CategoryRepository
**
๐ Infrastructure/Data/CategoryRepository.cs
using Core.Entities;
using Core.Interfaces;
namespace Infrastructure.Data
{
public class CategoryRepository : Repository<Category>, ICategoryRepository
{
public CategoryRepository(StoreContext context) : base(context) { }
}
}
7๏ธโฃ Register the Repositories in **Program.cs
**
๐ API/Program.cs
using Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Register StoreContext with Dependency Injection
builder.Services.AddDbContext<StoreContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register Repositories
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ICategoryRepository, CategoryRepository>();
var app = builder.Build();
// Apply Migrations Automatically (Optional: Good for development)
using var scope = app.Services.CreateScope();
var services = scope.ServiceProvider;
var context = services.GetRequiredService<StoreContext>();
context.Database.Migrate();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
8๏ธโฃ Create and Apply Migrations
Run the following commands in the API project folder:
# Create a Migration
dPM> dotnet ef migrations add AddCategoryEntity
No project was found. Change the current working directory or use the --project option.
# Apply the Migration to Update the Database
dotnet ef database update
๐ Step 2 is Complete
โ We refactored the Product Repository using a Generic Repository.\
โ We introduced the Category Repository, demonstrating reusability.\
โ The Generic Repository now enables easy expansion for future entities.\
โ We added and applied migrations to update the database.
๐ Whatโs Next?
In the next article, we will introduce the Specification Pattern to handle complex queries efficiently. This will allow filtering, sorting, and pagination without cluttering our repositories.
Stay tuned! ๐
Top comments (0)