π 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)