๐ Step 2.1: Create the Generic Repository Interface
๐ Core/Interfaces/IGenericRepository.cs
using Core.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Core.Interfaces
{
public interface IGenericRepository<T> where T : BaseEntity
{
Task<T?> GetByIdAsync(int id);
Task<IReadOnlyList<T>> ListAllAsync();
void Add(T entity);
void Update(T entity);
void Remove(T entity);
Task<bool> SaveAllAsync();
}
}
โ Works for any entity (Product
, Category
, etc.).
โ Ensures all entities have an Id
via BaseEntity
.
๐ Step 2.2: Create the Generic Repository Implementation
๐ Infrastructure/Data/GenericRepository.cs
using Core.Entities;
using Core.Interfaces;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Infrastructure.Data
{
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private readonly StoreContext _context;
public GenericRepository(StoreContext context)
{
_context = context;
}
public async Task<T?> GetByIdAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}
public async Task<IReadOnlyList<T>> ListAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
public void Add(T entity)
{
_context.Set<T>().Add(entity);
}
public void Update(T entity)
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Remove(T entity)
{
_context.Set<T>().Remove(entity);
}
public async Task<bool> SaveAllAsync()
{
return await _context.SaveChangesAsync() > 0;
}
}
}
โ Works for all entities (Product
, Category
, etc.).
โ Uses _context.Set<T>()
instead of _context.Products
.
๐ Step 2.3: Remove the ProductRepository
๐ Delete ProductRepository.cs
(No longer needed).
๐ Step 2.4: Implement the ProductsController
๐ API/Controllers/ProductsController.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Core.Entities;
using Core.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly IGenericRepository<Product> _productRepository;
public ProductsController(IGenericRepository<Product> productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public async Task<ActionResult<List<Product>>> GetProducts()
{
var products = await _productRepository.ListAllAsync();
return Ok(products);
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProductById(int id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return NotFound();
return Ok(product);
}
[HttpPost]
public async Task<ActionResult> CreateProduct(Product product)
{
_productRepository.Add(product);
await _productRepository.SaveAllAsync();
return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);
}
[HttpPut("{id}")]
public async Task<ActionResult> UpdateProduct(int id, Product product)
{
var existingProduct = await _productRepository.GetByIdAsync(id);
if (existingProduct == null) return NotFound();
_productRepository.Update(product);
await _productRepository.SaveAllAsync();
return NoContent();
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteProduct(int id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return NotFound();
_productRepository.Remove(product);
await _productRepository.SaveAllAsync();
return NoContent();
}
}
}
๐น What Changed?
โ Uses IGenericRepository<Product>
instead of IProductRepository
.
โ Uses traditional controllers with [ApiController]
instead of Minimal API.
โ No business logic inside controllers (keeps them clean and focused).
๐ Step 2.5: Configure Dependency Injection in Program.cs
๐ API/Program.cs
using Core.Interfaces;
using Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Configure Database Connection
builder.Services.AddDbContext<StoreContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register Generic Repository
builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
// Add Controllers
builder.Services.AddControllers();
var app = builder.Build();
// Middleware
app.UseAuthorization();
app.MapControllers();
app.Run();
๐น What Changed?
โ Uses builder.Services.AddControllers()
for controllers.
โ Registers Generic Repository with Dependency Injection:
builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
โ Uses .MapControllers()
instead of Minimal API routes.
๐ Step 2.6: Testing the API
โ Get All Products
GET /api/products
โ Returns all products.
โ Get Product By ID
GET /api/products/1
โ Returns product with ID = 1.
โ Create a Product
POST /api/products
Content-Type: application/json
{
"name": "Laptop",
"brand": "Dell",
"type": "Electronics",
"price": 1500
}
โ Creates a new product.
โ Update a Product
PUT /api/products/1
Content-Type: application/json
{
"id": 1,
"name": "Gaming Laptop",
"brand": "Dell",
"type": "Electronics",
"price": 2000
}
โ Updates product details.
โ Delete a Product
DELETE /api/products/1
โ Deletes product with ID = 1.
๐ Step 2 Complete!
โ
Replaced the Product Repository with a Generic Repository in .NET 8.
โ
Used Traditional Controllers with [ApiController]
.
โ
Kept the API clean, modular, and scalable.
๐ Step 3: Apply the Specification Pattern
Now that we have a Generic Repository, we will implement the Specification Pattern to add filtering, sorting, and pagination dynamically.
Would you like me to proceed with Step 3 in detail? ๐๐
Top comments (0)