π 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 ProductDto
for API Response
π API/DTOs/ProductDto.cs
namespace API.DTOs
{
public class ProductDto
{
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; }
}
}
4οΈβ£ 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();
}
}
5οΈβ£ 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;
}
}
}
6οΈβ£ 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; }
}
}
7οΈβ£ Create the API Controller: ProductsController
π API/Controllers/ProductsController.cs
using API.DTOs;
using Core.Entities;
using Core.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
{
var products = await _productRepository.ListAllAsync();
return Ok(products.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Brand = p.Brand,
Type = p.Type,
Price = p.Price
}));
}
[HttpGet("{id}")]
public async Task<ActionResult<ProductDto>> GetProduct(int id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return NotFound();
return Ok(new ProductDto
{
Id = product.Id,
Name = product.Name,
Brand = product.Brand,
Type = product.Type,
Price = product.Price
});
}
[HttpPost]
public async Task<ActionResult<ProductDto>> CreateProduct(ProductDto productDto)
{
var product = new Product
{
Name = productDto.Name,
Brand = productDto.Brand,
Type = productDto.Type,
Price = productDto.Price
};
_productRepository.Add(product);
await _productRepository.SaveAllAsync();
return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, productDto);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, ProductDto productDto)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return NotFound();
product.Name = productDto.Name;
product.Brand = productDto.Brand;
product.Type = productDto.Type;
product.Price = productDto.Price;
_productRepository.Update(product);
await _productRepository.SaveAllAsync();
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return NotFound();
_productRepository.Remove(product);
await _productRepository.SaveAllAsync();
return NoContent();
}
}
}
8οΈβ£ 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();
app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
π― Final Steps: Running the API
- Run the following commands to apply migrations:
dotnet ef migrations add InitialCreate --project ../Infrastructure/Data.csproj --startup-project API/ProductAPI.csproj
dotnet ef database update --project ../Infrastructure/Data.csproj --startup-project API/ProductAPI.csproj
- Start the API:
dotnet run
- Open Swagger UI at
https://localhost:5001/swagger
to test the endpoints.
π Step 1 is Complete
β We implemented a Product API using a traditional repository.
β The API can Create, Read, Update, and Delete (CRUD) products.
Next Steps: Implement the Generic Repository to improve code reusability. π
Top comments (0)