From 4a28685b68ee58fbdd6d4b1504b0ead077efbeee Mon Sep 17 00:00:00 2001 From: cshowalter Date: Tue, 10 May 2022 01:46:32 -0400 Subject: [PATCH 1/7] Delete Database Signed-off-by: cshowalter --- API/ecommerce.db | Bin 36864 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 API/ecommerce.db diff --git a/API/ecommerce.db b/API/ecommerce.db deleted file mode 100644 index 488241a9c8ffc797b403fc41bd8e84e7ed7ac17f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeI)PjAv-90%|gHVPyRZ%s_d9;?enwh)HS=}8$p%x0`0bcP)asZdQ523uQ{3t2=(|lRAT$8*V*e8Zd9tw8^ zQ52pNA_zi+tqHdB5@Q=ne22Y?6Wd{%5g}Lmm`eW^;)!p<%8&Hdl}ze;>TdE&GRqF2 zKmY;|fB*y_009WxEbu-q$Lq9(?)h<7pC^cwNr$p7t zv_+KA>lISdh{9_rWP@*ChEuZVk|Y;dyZ6G{-Ls8;_p0IejSUACLR;qNv!9%pBxf?> z=clgmrf2d_@>eNnNiXhI>C_lfNy3Vvb`>^$o$k{H(P}!;j;d8ss%cHVQDnzil>?6F zD5Ty{4~vasazKws@kprb=&H& z+Fi3_ofy51?I&A>?6oj;tK$xB^T_UnM)Bu7n44>Zb~YXjkATH&5{PK&<52?v^k!V%RpXku^GvF30J07%968GY?g{;8g%=H?s$V9 z_l671?F>)dd42Jtgm)#C%#L!)lDxJiUaU@LonJ3-^|xblvyX?reFg?KZzwu2b6wjp&xU5dW6ta@Pj!0cQ_h4jt5Oc3# z850a<9MikIOx+A-Z!IB#CWbcietl)y&hPe1Rwwb2tWV=5P$## zAOHafKp+sn^FLAn0uX=z1Rwwb2tWV=5P$##Ah7rXc>Z7fImQSf009U<00Izz00bZa z0SG_<0^a}s!{7fS400bZa0SG_< N0uX=z1R&rE`~kM05jFq- From 454b9fda49d2ef5fbba4fe54863cd980fbb2cee5 Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 07:37:05 -0700 Subject: [PATCH 2/7] issue fixed --- API/ecommerce.db-shm | Bin 32768 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 API/ecommerce.db-shm diff --git a/API/ecommerce.db-shm b/API/ecommerce.db-shm deleted file mode 100644 index 09e3ea49c5f14c160c9199f2c9f7a89b1388766b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI)!3n}Z5Cu?O;@xAKu!!{FNzg)4h+T*U1Oh2QD-Z-PiXC_|J1XMUqvm}WW?7b5 zcm+&(nvEjkXH8S?lU%p$;ks_B`h476mWw9sQrGV1tGfL1eIE5l%)Ni)b^UHdKE=O6 zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK;Rz*V&9h#7^XmMqZ0xI2oNAZfB*pk x1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!Ct+2=u28E2RJc From 74078ede9e1d22c174fddd42f89cc57999a227a0 Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 09:07:07 -0700 Subject: [PATCH 3/7] Added Seed Data --- Infrastructure/Data/StoreContextSeed.cs | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Infrastructure/Data/StoreContextSeed.cs diff --git a/Infrastructure/Data/StoreContextSeed.cs b/Infrastructure/Data/StoreContextSeed.cs new file mode 100644 index 0000000..de4c058 --- /dev/null +++ b/Infrastructure/Data/StoreContextSeed.cs @@ -0,0 +1,57 @@ +using System.Text.Json; +using Core.Entities; +using Infrastructure.Data; +using Microsoft.Extensions.Logging; + +namespace Infrastructure +{ + public class StoreContextSeed + { + public static async Task SeedAsync(StoreContext context, ILoggerFactory loggerFactory) + { + try + { + if (!context.ProductBrands.Any()) + { + var brandsData = File.ReadAllText("../Infrastructure/Data/SeedData/brands.json"); + var brands = JsonSerializer.Deserialize>(brandsData); + foreach (var item in brands) + { + context.ProductBrands.Add(item); + } + + await context.SaveChangesAsync(); + } + + if (!context.ProductTypes.Any()) + { + var typesData = File.ReadAllText("../Infrastructure/Data/SeedData/types.json"); + var types = JsonSerializer.Deserialize>(typesData); + foreach (var item in types) + { + context.ProductTypes.Add(item); + } + + await context.SaveChangesAsync(); + } + + if (!context.Products.Any()) + { + var productsData = File.ReadAllText("../Infrastructure/Data/SeedData/products.json"); + var products = JsonSerializer.Deserialize>(productsData); + foreach (var item in products) + { + context.Products.Add(item); + } + + await context.SaveChangesAsync(); + } + } + catch (Exception ex) + { + var logger = loggerFactory.CreateLogger(); + logger.LogError(ex.Message); + } + } + } +} \ No newline at end of file From f185ef91630fa324e2031977a12d7beb1b6e7ac2 Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 09:07:23 -0700 Subject: [PATCH 4/7] Added Seed Data --- API/Program.cs | 1 + Infrastructure/Data/SeedData/brands.json | 26 ++++ Infrastructure/Data/SeedData/products.json | 146 +++++++++++++++++++++ Infrastructure/Data/SeedData/types.json | 18 +++ 4 files changed, 191 insertions(+) create mode 100644 Infrastructure/Data/SeedData/brands.json create mode 100644 Infrastructure/Data/SeedData/products.json create mode 100644 Infrastructure/Data/SeedData/types.json diff --git a/API/Program.cs b/API/Program.cs index 2719f5e..04442c8 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -16,6 +16,7 @@ namespace API { var context = services.GetRequiredService(); await context.Database.MigrateAsync(); + await Infrastructure.StoreContextSeed.SeedAsync(context, loggerFactory); } catch (Exception ex) { diff --git a/Infrastructure/Data/SeedData/brands.json b/Infrastructure/Data/SeedData/brands.json new file mode 100644 index 0000000..592b241 --- /dev/null +++ b/Infrastructure/Data/SeedData/brands.json @@ -0,0 +1,26 @@ +[ + { + "Id": 1, + "Name": "Angular" + }, + { + "Id": 2, + "Name": "NetCore" + }, + { + "Id": 3, + "Name": "VS Code" + }, + { + "Id": 4, + "Name": "React" + }, + { + "Id": 5, + "Name": "Typescript" + }, + { + "Id": 6, + "Name": "Redis" + } +] \ No newline at end of file diff --git a/Infrastructure/Data/SeedData/products.json b/Infrastructure/Data/SeedData/products.json new file mode 100644 index 0000000..600ecfc --- /dev/null +++ b/Infrastructure/Data/SeedData/products.json @@ -0,0 +1,146 @@ +[ + { + "Name": "Angular Speedster Board 2000", + "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", + "Price": 200, + "PictureUrl": "images/products/sb-ang1.png", + "ProductTypeId": 1, + "ProductBrandId": 1 + }, + { + "Name": "Green Angular Board 3000", + "Description": "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.", + "Price": 150, + "PictureUrl": "images/products/sb-ang2.png", + "ProductTypeId": 1, + "ProductBrandId": 1 + }, + { + "Name": "Core Board Speed Rush 3", + "Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.", + "Price": 180, + "PictureUrl": "images/products/sb-core1.png", + "ProductTypeId": 1, + "ProductBrandId": 2 + }, + { + "Name": "Net Core Super Board", + "Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.", + "Price": 300, + "PictureUrl": "images/products/sb-core2.png", + "ProductTypeId": 1, + "ProductBrandId": 2 + }, + { + "Name": "React Board Super Whizzy Fast", + "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", + "Price": 250, + "PictureUrl": "images/products/sb-react1.png", + "ProductTypeId": 1, + "ProductBrandId": 4 + }, + { + "Name": "Typescript Entry Board", + "Description": "Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.", + "Price": 120, + "PictureUrl": "images/products/sb-ts1.png", + "ProductTypeId": 1, + "ProductBrandId": 5 + }, + { + "Name": "Core Blue Hat", + "Description": "Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", + "Price": 10, + "PictureUrl": "images/products/hat-core1.png", + "ProductTypeId": 2, + "ProductBrandId": 2 + }, + { + "Name": "Green React Woolen Hat", + "Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.", + "Price": 8, + "PictureUrl": "images/products/hat-react1.png", + "ProductTypeId": 2, + "ProductBrandId": 4 + }, + { + "Name": "Purple React Woolen Hat", + "Description": "Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", + "Price": 15, + "PictureUrl": "images/products/hat-react2.png", + "ProductTypeId": 2, + "ProductBrandId": 4 + }, + { + "Name": "Blue Code Gloves", + "Description": "Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.", + "Price": 18, + "PictureUrl": "images/products/glove-code1.png", + "ProductTypeId": 4, + "ProductBrandId": 3 + }, + { + "Name": "Green Code Gloves", + "Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.", + "Price": 15, + "PictureUrl": "images/products/glove-code2.png", + "ProductTypeId": 4, + "ProductBrandId": 3 + }, + { + "Name": "Purple React Gloves", + "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa.", + "Price": 16, + "PictureUrl": "images/products/glove-react1.png", + "ProductTypeId": 4, + "ProductBrandId": 4 + }, + { + "Name": "Green React Gloves", + "Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.", + "Price": 14, + "PictureUrl": "images/products/glove-react2.png", + "ProductTypeId": 4, + "ProductBrandId": 4 + }, + { + "Name": "Redis Red Boots", + "Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.", + "Price": 250, + "PictureUrl": "images/products/boot-redis1.png", + "ProductTypeId": 3, + "ProductBrandId": 6 + }, + { + "Name": "Core Red Boots", + "Description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.", + "Price": 189.99, + "PictureUrl": "images/products/boot-core2.png", + "ProductTypeId": 3, + "ProductBrandId": 2 + }, + { + "Name": "Core Purple Boots", + "Description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.", + "Price": 199.99, + "PictureUrl": "images/products/boot-core1.png", + "ProductTypeId": 3, + "ProductBrandId": 2 + }, + { + "Name": "Angular Purple Boots", + "Description": "Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.", + "Price": 150, + "PictureUrl": "images/products/boot-ang2.png", + "ProductTypeId": 3, + "ProductBrandId": 1 + }, + { + "Name": "Angular Blue Boots", + "Description": "Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.", + "Price": 180, + "PictureUrl": "images/products/boot-ang1.png", + "ProductTypeId": 3, + "ProductBrandId": 1 + } +] \ No newline at end of file diff --git a/Infrastructure/Data/SeedData/types.json b/Infrastructure/Data/SeedData/types.json new file mode 100644 index 0000000..9ae0d91 --- /dev/null +++ b/Infrastructure/Data/SeedData/types.json @@ -0,0 +1,18 @@ +[ + { + "Id": 1, + "Name": "Boards" + }, + { + "Id": 2, + "Name": "Hats" + }, + { + "Id": 3, + "Name": "Boots" + }, + { + "Id": 4, + "Name": "Gloves" + } +] \ No newline at end of file From 7601a394b4d1fb0e347b3ec222a23b43cb94364c Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 10:26:43 -0700 Subject: [PATCH 5/7] Added Brand and Types to Controller --- .gitignore | 2 +- API/Controllers/ProductsController.cs | 13 +++++++++++++ Core/Interfaces/iProductRepository.cs | 2 ++ Infrastructure/Data/ProductRepository.cs | 20 ++++++++++++++++++-- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 454729b..6950d24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ obj bin appsettings.json -*.db \ No newline at end of file +*.db* \ No newline at end of file diff --git a/API/Controllers/ProductsController.cs b/API/Controllers/ProductsController.cs index 735d3e8..a3313eb 100644 --- a/API/Controllers/ProductsController.cs +++ b/API/Controllers/ProductsController.cs @@ -27,5 +27,18 @@ namespace API.Controllers { return await _repo.GetProductByIdAsync(id); } + + [HttpGet("brands")] + public async Task>> GetProductBrands() + { + return Ok(await _repo.GetProductBrandsAsync()); + } + + [HttpGet("types")] + public async Task>> GetProductTypes() + { + return Ok(await _repo.GetProductTypesAsync()); + + } } } \ No newline at end of file diff --git a/Core/Interfaces/iProductRepository.cs b/Core/Interfaces/iProductRepository.cs index 5e63b17..ce9b413 100644 --- a/Core/Interfaces/iProductRepository.cs +++ b/Core/Interfaces/iProductRepository.cs @@ -6,5 +6,7 @@ namespace Core.Interfaces { Task GetProductByIdAsync(int id); Task> GetProductsAync(); + Task> GetProductBrandsAsync(); + Task> GetProductTypesAsync(); } } \ No newline at end of file diff --git a/Infrastructure/Data/ProductRepository.cs b/Infrastructure/Data/ProductRepository.cs index cf7b2f1..88e70e1 100644 --- a/Infrastructure/Data/ProductRepository.cs +++ b/Infrastructure/Data/ProductRepository.cs @@ -12,14 +12,30 @@ namespace Infrastructure.Data _context = context; } + public async Task> GetProductBrandsAsync() + { + return await _context.ProductBrands.ToListAsync(); + } + public async Task GetProductByIdAsync(int id) { - return await _context.Products.FindAsync(id); + return await _context.Products + .Include(p => p.ProductType) + .Include(p => p.ProductBrand) + .FirstOrDefaultAsync(p => p.Id == id); } public async Task> GetProductsAync() { - return await _context.Products.ToListAsync(); + return await _context.Products + .Include(p => p.ProductType) + .Include(p => p.ProductBrand) + .ToListAsync(); + } + + public async Task> GetProductTypesAsync() + { + return await _context.ProductTypes.ToListAsync(); } } } \ No newline at end of file From 8bca3f1f9d8cc42f35951e0640ccff2529e8b4f3 Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 15:45:47 -0700 Subject: [PATCH 6/7] Added Generics --- API/Controllers/ProductsController.cs | 23 +++++++---- API/Program.cs | 3 +- API/Startup.cs | 1 + Core/Interfaces/IGenericRepository.cs | 13 ++++++ Core/Specifications/BaseSpecification.cs | 23 +++++++++++ Core/Specifications/ISpecification.cs | 11 +++++ ...ProductsWithTypesAndBrandsSpecification.cs | 20 +++++++++ Infrastructure/Data/GenericRepository.cs | 41 +++++++++++++++++++ Infrastructure/Data/SpecificationEvaluator.cs | 23 +++++++++++ 9 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 Core/Interfaces/IGenericRepository.cs create mode 100644 Core/Specifications/BaseSpecification.cs create mode 100644 Core/Specifications/ISpecification.cs create mode 100644 Core/Specifications/ProductsWithTypesAndBrandsSpecification.cs create mode 100644 Infrastructure/Data/GenericRepository.cs create mode 100644 Infrastructure/Data/SpecificationEvaluator.cs 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 From ea9c8dbb2690196b11e3d7ddf7d00a364eae76be Mon Sep 17 00:00:00 2001 From: Charles Showalter Date: Tue, 10 May 2022 16:49:42 -0700 Subject: [PATCH 7/7] Incorporated AutoMapper --- API/API.csproj | 1 + API/Controllers/ProductsController.cs | 15 ++++++++++----- API/Dtos/ProductToReturnDto.cs | 18 ++++++++++++++++++ API/Helpers/MappingProfiles.cs | 16 ++++++++++++++++ API/Startup.cs | 2 ++ 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 API/Dtos/ProductToReturnDto.cs create mode 100644 API/Helpers/MappingProfiles.cs diff --git a/API/API.csproj b/API/API.csproj index 5bc8cfe..dda2eff 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -6,6 +6,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/API/Controllers/ProductsController.cs b/API/Controllers/ProductsController.cs index 374c947..fcd46f9 100644 --- a/API/Controllers/ProductsController.cs +++ b/API/Controllers/ProductsController.cs @@ -2,6 +2,8 @@ using Core.Entities; using Microsoft.AspNetCore.Mvc; using Core.Interfaces; using Core.Specifications; +using API.Dtos; +using AutoMapper; namespace API.Controllers { @@ -12,8 +14,10 @@ namespace API.Controllers private readonly IGenericRepository _productsRepo; private readonly IGenericRepository _productBrandRepo; private readonly IGenericRepository _productTypeRepo; - public ProductsController(IGenericRepository productsRepo, IGenericRepository productBrandRepo, IGenericRepository productTypeRepo) + private readonly IMapper _mapper; + public ProductsController(IGenericRepository productsRepo, IGenericRepository productBrandRepo, IGenericRepository productTypeRepo, IMapper mapper) { + _mapper = mapper; _productTypeRepo = productTypeRepo; _productBrandRepo = productBrandRepo; _productsRepo = productsRepo; @@ -21,18 +25,19 @@ namespace API.Controllers } [HttpGet] - public async Task>> GetProducts() + public async Task>> GetProducts() { var spec = new ProductsWithTypesAndBrandsSpecification(); var products = await _productsRepo.ListAsync(spec); - return Ok(products); + return Ok(_mapper.Map, IReadOnlyList>(products)); } [HttpGet("{id}")] - public async Task> GetProduct(int id) + public async Task> GetProduct(int id) { var spec = new ProductsWithTypesAndBrandsSpecification(id); - return await _productsRepo.GetEntityWithSpec(spec); + var product = await _productsRepo.GetEntityWithSpec(spec); + return _mapper.Map(product); } [HttpGet("brands")] diff --git a/API/Dtos/ProductToReturnDto.cs b/API/Dtos/ProductToReturnDto.cs new file mode 100644 index 0000000..7a204d8 --- /dev/null +++ b/API/Dtos/ProductToReturnDto.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace API.Dtos +{ + public class ProductToReturnDto + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public decimal Price { get; set; } + public string PictureUrl { get; set; } + public string ProductType { get; set; } + public string ProductBrand { get; set; } + } +} \ No newline at end of file diff --git a/API/Helpers/MappingProfiles.cs b/API/Helpers/MappingProfiles.cs new file mode 100644 index 0000000..1edaa9d --- /dev/null +++ b/API/Helpers/MappingProfiles.cs @@ -0,0 +1,16 @@ +using API.Dtos; +using AutoMapper; +using Core.Entities; + +namespace API.Helpers +{ + public class MappingProfiles : Profile + { + public MappingProfiles() + { + CreateMap() + .ForMember(d => d.ProductBrand, o => o.MapFrom(s => s.ProductBrand.Name)) + .ForMember(d => d.ProductType, o => o.MapFrom(s => s.ProductType.Name)); + } + } +} \ No newline at end of file diff --git a/API/Startup.cs b/API/Startup.cs index aa263cb..8d9d6fc 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -1,3 +1,4 @@ +using API.Helpers; using Core.Interfaces; using Infrastructure.Data; using Microsoft.EntityFrameworkCore; @@ -22,6 +23,7 @@ namespace API services.AddDbContext(x => x.UseSqlite(_config.GetConnectionString("DefaultConnection"))); services.AddScoped(); services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>))); + services.AddAutoMapper(typeof(MappingProfiles)); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebAPIv5", Version = "v1" });