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
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
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; }
}
}
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();
}
}
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;
}
}
}
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; }
}
}
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();
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": "*"
}
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
π 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)