Specification Pattern P2
mohamed Tayel

mohamed Tayel @moh_moh701

About: Technical Project Lead at SURE Egypt with 20+ years in software development. Specializes in .Net, and SQL Server. Passionate about delivering quality solutions

Joined:
Feb 5, 2024

Specification Pattern P2

Publish Date: Feb 2
0 0

🚀 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! 🚀

Comments 0 total

    Add comment