π 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)