DEV Community

mohamed Tayel
mohamed Tayel

Posted on β€’ Edited on

Specification Pattern P2

πŸš€ 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
Enter fullscreen mode Exit fullscreen mode

1️⃣ Define the Base Entity

πŸ“‚ Core/Entities/BaseEntity.cs

namespace Core.Entities
{
    public abstract class BaseEntity
    {
        public int Id { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

4️⃣ Define the **Category**** Entity**

πŸ“‚ Core/Entities/Category.cs

namespace Core.Entities
{
    public class Category : BaseEntity
    {
        public string Name { get; set; } = string.Empty;
    }
}
Enter fullscreen mode Exit fullscreen mode

5️⃣ Implement the **ICategoryRepository**** Interface**

πŸ“‚ Core/Interfaces/ICategoryRepository.cs

using Core.Entities;

namespace Core.Interfaces
{
    public interface ICategoryRepository : IRepository<Category>
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

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) { }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

πŸš€ 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! πŸš€

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay