DEV Community

mohamed Tayel
mohamed Tayel

Posted on

Specification Pattern P1

We will go step by step, first using a traditional repository, then applying the Generic Repository, and finally implementing the Specification Pattern.


πŸš€ Step 1: Case Study Without Generic Repository & Specification Pattern

πŸ’‘ Goal: Implement a simple Product API using a separate repository for products.

πŸ“‚ Project Structure (Without Generic Repository & Specification Pattern)

πŸ“‚ ProductApp
 β”œβ”€β”€ πŸ“‚ Core                 # Domain Layer
 β”‚    β”œβ”€β”€ πŸ“‚ Entities
 β”‚    β”‚    β”œβ”€β”€ Product.cs
 β”‚    β”œβ”€β”€ πŸ“‚ Interfaces
 β”‚    β”‚    β”œβ”€β”€ IProductRepository.cs
 β”‚
 β”œβ”€β”€ πŸ“‚ Infrastructure       # Data Access Layer
 β”‚    β”œβ”€β”€ πŸ“‚ Data
 β”‚    β”‚    β”œβ”€β”€ ProductRepository.cs
 β”‚    β”‚    β”œβ”€β”€ StoreContext.cs
 β”‚
 β”œβ”€β”€ πŸ“‚ API                  # Presentation Layer
 β”‚    β”œβ”€β”€ πŸ“‚ Controllers
 β”‚    β”‚    β”œβ”€β”€ ProductsController.cs
 β”‚    β”œβ”€β”€ πŸ“‚ DTOs
 β”‚    β”‚    β”œβ”€β”€ ProductDto.cs
Enter fullscreen mode Exit fullscreen mode

1️⃣ Install Required NuGet Packages

Before proceeding, install the necessary NuGet packages for Entity Framework Core and SQL Server support:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
Enter fullscreen mode Exit fullscreen mode

2️⃣ Create the Product Entity

πŸ“‚ Core/Entities/Product.cs

namespace Core.Entities
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string Brand { get; set; } = string.Empty;
        public string Type { get; set; } = string.Empty;
        public decimal Price { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ Create the IProductRepository Interface

πŸ“‚ Core/Interfaces/IProductRepository.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using Core.Entities;

namespace Core.Interfaces
{
    public interface IProductRepository
    {
        Task<Product?> GetByIdAsync(int id);
        Task<List<Product>> ListAllAsync();
        void Add(Product product);
        void Update(Product product);
        void Remove(Product product);
        Task<bool> SaveAllAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

4️⃣ Implement the ProductRepository

πŸ“‚ Infrastructure/Data/ProductRepository.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using Core.Entities;
using Core.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Data
{
    public class ProductRepository : IProductRepository
    {
        private readonly StoreContext _context;

        public ProductRepository(StoreContext context)
        {
            _context = context;
        }

        public async Task<Product?> GetByIdAsync(int id)
        {
            return await _context.Products.FindAsync(id);
        }

        public async Task<List<Product>> ListAllAsync()
        {
            return await _context.Products.ToListAsync();
        }

        public void Add(Product product)
        {
            _context.Products.Add(product);
        }

        public void Update(Product product)
        {
            _context.Products.Update(product);
        }

        public void Remove(Product product)
        {
            _context.Products.Remove(product);
        }

        public async Task<bool> SaveAllAsync()
        {
            return await _context.SaveChangesAsync() > 0;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

5️⃣ Create the StoreContext (DbContext)

πŸ“‚ Infrastructure/Data/StoreContext.cs

using Core.Entities;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Data
{
    public class StoreContext : DbContext
    {
        public StoreContext(DbContextOptions<StoreContext> options) : base(options) { }

        public DbSet<Product> Products { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

6️⃣ Register StoreContext 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 Repository
builder.Services.AddScoped<IProductRepository, ProductRepository>();

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

7️⃣ Add Connection String to appsettings.json

πŸ“‚ API/appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=StoreDb;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Enter fullscreen mode Exit fullscreen mode

8️⃣ Create and Apply Migrations

Run the following commands in the API project folder:

# Create a Migration
dotnet ef migrations add InitialCreate --project ../Data/Data.csproj --startup-project API/ProductAPI.csproj

# Apply the Migration to Create the Database
dotnet ef database update --project ../Data/Data.csproj --startup-project API/ProductAPI.csproj
Enter fullscreen mode Exit fullscreen mode

πŸš€ Step 1 is Complete

βœ” We implemented a Product API using a traditional repository (without Generic Repository or Specification Pattern).\
βœ” The repository directly handles product-related queries.\
βœ” The API can create, read, update, and delete products.

Next Steps: Implement the Generic Repository to improve code reusability. πŸš€

Top comments (0)