diff --git a/API/Controllers/ProductsController.cs b/API/Controllers/ProductsController.cs index a3313eb..374c947 100644 --- a/API/Controllers/ProductsController.cs +++ b/API/Controllers/ProductsController.cs @@ -1,6 +1,7 @@ using Core.Entities; using Microsoft.AspNetCore.Mvc; using Core.Interfaces; +using Core.Specifications; namespace API.Controllers { @@ -8,36 +9,42 @@ namespace API.Controllers [Route("api/[controller]")] public class ProductsController : ControllerBase { - private readonly iProductRepository _repo; - public ProductsController(iProductRepository repo) + private readonly IGenericRepository _productsRepo; + private readonly IGenericRepository _productBrandRepo; + private readonly IGenericRepository _productTypeRepo; + public ProductsController(IGenericRepository productsRepo, IGenericRepository productBrandRepo, IGenericRepository productTypeRepo) { - _repo = repo; + _productTypeRepo = productTypeRepo; + _productBrandRepo = productBrandRepo; + _productsRepo = productsRepo; + } [HttpGet] public async Task>> GetProducts() { - var products = await _repo.GetProductsAync(); - + var spec = new ProductsWithTypesAndBrandsSpecification(); + var products = await _productsRepo.ListAsync(spec); return Ok(products); } [HttpGet("{id}")] public async Task> GetProduct(int id) { - return await _repo.GetProductByIdAsync(id); + var spec = new ProductsWithTypesAndBrandsSpecification(id); + return await _productsRepo.GetEntityWithSpec(spec); } [HttpGet("brands")] public async Task>> GetProductBrands() { - return Ok(await _repo.GetProductBrandsAsync()); + return Ok(await _productBrandRepo.ListAllAsync()); } [HttpGet("types")] public async Task>> GetProductTypes() { - return Ok(await _repo.GetProductTypesAsync()); + return Ok(await _productTypeRepo.ListAllAsync()); } } diff --git a/API/Program.cs b/API/Program.cs index 04442c8..11a5d97 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -1,3 +1,4 @@ +using Infrastructure; using Infrastructure.Data; using Microsoft.EntityFrameworkCore; @@ -16,7 +17,7 @@ namespace API { var context = services.GetRequiredService(); await context.Database.MigrateAsync(); - await Infrastructure.StoreContextSeed.SeedAsync(context, loggerFactory); + await StoreContextSeed.SeedAsync(context, loggerFactory); } catch (Exception ex) { diff --git a/API/Startup.cs b/API/Startup.cs index 742c42f..aa263cb 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -21,6 +21,7 @@ namespace API services.AddControllers(); services.AddDbContext(x => x.UseSqlite(_config.GetConnectionString("DefaultConnection"))); services.AddScoped(); + services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>))); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebAPIv5", Version = "v1" }); diff --git a/Core/Interfaces/IGenericRepository.cs b/Core/Interfaces/IGenericRepository.cs new file mode 100644 index 0000000..10ad70f --- /dev/null +++ b/Core/Interfaces/IGenericRepository.cs @@ -0,0 +1,13 @@ +using Core.Entities; +using Core.Specifications; + +namespace Core.Interfaces +{ + public interface IGenericRepository where T : BaseEntity + { + Task GetByIdAsync(int id); + Task> ListAllAsync(); + Task GetEntityWithSpec(ISpecification spec); + Task> ListAsync(ISpecification spec); + } +} \ No newline at end of file diff --git a/Core/Specifications/BaseSpecification.cs b/Core/Specifications/BaseSpecification.cs new file mode 100644 index 0000000..9c241a9 --- /dev/null +++ b/Core/Specifications/BaseSpecification.cs @@ -0,0 +1,23 @@ +using System.Linq.Expressions; + +namespace Core.Specifications +{ + public class BaseSpecification : ISpecification + { + public BaseSpecification() + { + } + + public BaseSpecification(Expression> criteria) + { + Criteria = criteria; + } + public Expression> Criteria { get; } + public List>> Includes { get; } = + new List>>(); + protected void AddInclude(Expression> includeExpression) + { + Includes.Add(includeExpression); + } + } +} \ No newline at end of file diff --git a/Core/Specifications/ISpecification.cs b/Core/Specifications/ISpecification.cs new file mode 100644 index 0000000..b08c329 --- /dev/null +++ b/Core/Specifications/ISpecification.cs @@ -0,0 +1,11 @@ +using System.Linq.Expressions; + +namespace Core.Specifications +{ + public interface ISpecification + { + Expression> Criteria {get; } + List>> Includes {get; } + + } +} \ No newline at end of file diff --git a/Core/Specifications/ProductsWithTypesAndBrandsSpecification.cs b/Core/Specifications/ProductsWithTypesAndBrandsSpecification.cs new file mode 100644 index 0000000..4a6b08a --- /dev/null +++ b/Core/Specifications/ProductsWithTypesAndBrandsSpecification.cs @@ -0,0 +1,20 @@ +using System.Linq.Expressions; +using Core.Entities; + +namespace Core.Specifications +{ + public class ProductsWithTypesAndBrandsSpecification : BaseSpecification + { + public ProductsWithTypesAndBrandsSpecification() + { + AddInclude(x => x.ProductType); + AddInclude(x => x.ProductBrand); + } + + public ProductsWithTypesAndBrandsSpecification(int id) : base(x => x.Id == id) + { + AddInclude(x => x.ProductType); + AddInclude(x => x.ProductBrand); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Data/GenericRepository.cs b/Infrastructure/Data/GenericRepository.cs new file mode 100644 index 0000000..90df1b1 --- /dev/null +++ b/Infrastructure/Data/GenericRepository.cs @@ -0,0 +1,41 @@ +using Core.Entities; +using Core.Interfaces; +using Core.Specifications; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Data +{ + public class GenericRepository : IGenericRepository where T : BaseEntity + { + private readonly StoreContext _context; + public GenericRepository(StoreContext context) + { + _context = context; + } + + public async Task GetByIdAsync(int id) + { + return await _context.Set().FindAsync(id); + } + + + public async Task> ListAllAsync() + { + return await _context.Set().ToListAsync(); + } + public async Task GetEntityWithSpec(ISpecification spec) + { + return await ApplySpecification(spec).FirstOrDefaultAsync(); + } + + public async Task> ListAsync(ISpecification spec) + { + return await ApplySpecification(spec).ToListAsync(); + } + + private IQueryable ApplySpecification(ISpecification spec) + { + return SpecificationEvaluator.GetQuery(_context.Set().AsQueryable(), spec); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Data/SpecificationEvaluator.cs b/Infrastructure/Data/SpecificationEvaluator.cs new file mode 100644 index 0000000..a59d0b2 --- /dev/null +++ b/Infrastructure/Data/SpecificationEvaluator.cs @@ -0,0 +1,23 @@ +using Core.Entities; +using Core.Specifications; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Data +{ + public class SpecificationEvaluator where TEntity : BaseEntity + { + public static IQueryable GetQuery(IQueryable inputQuery, ISpecification spec) + { + var query = inputQuery; + + if (spec.Criteria != null) + { + query = query.Where(spec.Criteria); + } + + query = spec.Includes.Aggregate(query, (current, include) => current.Include(include)); + + return query; + } + } +} \ No newline at end of file