diff --git a/src/Supermarket.API/Controllers/CategoriesController.cs b/src/Supermarket.API/Controllers/CategoriesController.cs
index a21bf8d..418f6ea 100644
--- a/src/Supermarket.API/Controllers/CategoriesController.cs
+++ b/src/Supermarket.API/Controllers/CategoriesController.cs
@@ -5,26 +5,20 @@
namespace Supermarket.API.Controllers
{
- public class CategoriesController : BaseApiController
+ public class CategoriesController(IServiceManager serviceManager, IMapper mapper) : BaseApiController
{
- private readonly ICategoryService _categoryService;
- private readonly IMapper _mapper;
+ private readonly IServiceManager _serviceManager = serviceManager;
+ private readonly IMapper _mapper = mapper;
- public CategoriesController(ICategoryService categoryService, IMapper mapper)
- {
- _categoryService = categoryService;
- _mapper = mapper;
- }
-
- ///
- /// Lists all categories.
- ///
- /// List os categories.
- [HttpGet]
+ ///
+ /// Lists all categories.
+ ///
+ /// List os categories.
+ [HttpGet]
[ProducesResponseType(typeof(IEnumerable), 200)]
public async Task> ListAsync()
{
- var categories = await _categoryService.ListAsync();
+ var categories = await _serviceManager.CategoryService.ListAsync();
return _mapper.Map>(categories);
}
@@ -39,7 +33,7 @@ public async Task> ListAsync()
public async Task PostAsync([FromBody] SaveCategoryResource resource)
{
var category = _mapper.Map(resource);
- var result = await _categoryService.SaveAsync(category);
+ var result = await _serviceManager.CategoryService.SaveAsync(category);
if (!result.Success)
{
@@ -62,7 +56,7 @@ public async Task PostAsync([FromBody] SaveCategoryResource resou
public async Task PutAsync(int id, [FromBody] SaveCategoryResource resource)
{
var category = _mapper.Map(resource);
- var result = await _categoryService.UpdateAsync(id, category);
+ var result = await _serviceManager.CategoryService.UpdateAsync(id, category);
if (!result.Success)
{
@@ -83,7 +77,7 @@ public async Task PutAsync(int id, [FromBody] SaveCategoryResourc
[ProducesResponseType(typeof(ErrorResource), 400)]
public async Task DeleteAsync(int id)
{
- var result = await _categoryService.DeleteAsync(id);
+ var result = await _serviceManager.CategoryService.DeleteAsync(id);
if (!result.Success)
{
diff --git a/src/Supermarket.API/Controllers/ProductsController.cs b/src/Supermarket.API/Controllers/ProductsController.cs
index ed45787..2df06bd 100644
--- a/src/Supermarket.API/Controllers/ProductsController.cs
+++ b/src/Supermarket.API/Controllers/ProductsController.cs
@@ -5,16 +5,10 @@
namespace Supermarket.API.Controllers
{
- public class ProductsController : BaseApiController
+ public class ProductsController(IServiceManager serviceManager, IMapper mapper) : BaseApiController
{
- private readonly IProductService _productService;
- private readonly IMapper _mapper;
-
- public ProductsController(IProductService productService, IMapper mapper)
- {
- _productService = productService;
- _mapper = mapper;
- }
+ private readonly IServiceManager _serviceManager = serviceManager;
+ private readonly IMapper _mapper = mapper;
///
/// Lists all existing products according to query filters.
@@ -25,7 +19,7 @@ public ProductsController(IProductService productService, IMapper mapper)
public async Task> ListAsync([FromQuery] ProductsQueryResource query)
{
var productsQuery = _mapper.Map(query);
- var queryResult = await _productService.ListAsync(productsQuery);
+ var queryResult = await _serviceManager.ProductService.ListAsync(productsQuery);
return _mapper.Map>(queryResult);
}
@@ -41,7 +35,7 @@ public async Task> ListAsync([FromQuery] Pr
public async Task PostAsync([FromBody] SaveProductResource resource)
{
var product = _mapper.Map(resource);
- var result = await _productService.SaveAsync(product);
+ var result = await _serviceManager.ProductService.SaveAsync(product);
if (!result.Success)
{
@@ -64,7 +58,7 @@ public async Task PostAsync([FromBody] SaveProductResource resour
public async Task PutAsync(int id, [FromBody] SaveProductResource resource)
{
var product = _mapper.Map(resource);
- var result = await _productService.UpdateAsync(id, product);
+ var result = await _serviceManager.ProductService.UpdateAsync(id, product);
if (!result.Success)
{
@@ -85,7 +79,7 @@ public async Task PutAsync(int id, [FromBody] SaveProductResource
[ProducesResponseType(typeof(ErrorResource), 400)]
public async Task DeleteAsync(int id)
{
- var result = await _productService.DeleteAsync(id);
+ var result = await _serviceManager.ProductService.DeleteAsync(id);
if (!result.Success)
{
diff --git a/src/Supermarket.API/Domain/Repositories/IUnitOfWork.cs b/src/Supermarket.API/Domain/Repositories/IUnitOfWork.cs
index 98706ad..b7b08d3 100644
--- a/src/Supermarket.API/Domain/Repositories/IUnitOfWork.cs
+++ b/src/Supermarket.API/Domain/Repositories/IUnitOfWork.cs
@@ -2,6 +2,8 @@ namespace Supermarket.API.Domain.Repositories
{
public interface IUnitOfWork
{
+ IProductRepository ProductRepository { get; }
+ ICategoryRepository CategoryRepository { get; }
Task CompleteAsync();
}
}
\ No newline at end of file
diff --git a/src/Supermarket.API/Domain/Services/IServiceManager.cs b/src/Supermarket.API/Domain/Services/IServiceManager.cs
new file mode 100644
index 0000000..a72e1d4
--- /dev/null
+++ b/src/Supermarket.API/Domain/Services/IServiceManager.cs
@@ -0,0 +1,8 @@
+namespace Supermarket.API.Domain.Services
+{
+ public interface IServiceManager
+ {
+ IProductService ProductService { get; }
+ ICategoryService CategoryService { get; }
+ }
+}
diff --git a/src/Supermarket.API/Persistence/Repositories/BaseRepository.cs b/src/Supermarket.API/Persistence/Repositories/BaseRepository.cs
index daf5b3a..dfe256e 100644
--- a/src/Supermarket.API/Persistence/Repositories/BaseRepository.cs
+++ b/src/Supermarket.API/Persistence/Repositories/BaseRepository.cs
@@ -2,13 +2,8 @@
namespace Supermarket.API.Persistence.Repositories
{
- public abstract class BaseRepository
+ public abstract class BaseRepository(AppDbContext context)
{
- protected readonly AppDbContext _context;
-
- public BaseRepository(AppDbContext context)
- {
- _context = context;
- }
+ protected readonly AppDbContext _context = context;
}
}
\ No newline at end of file
diff --git a/src/Supermarket.API/Persistence/Repositories/UnitOfWork.cs b/src/Supermarket.API/Persistence/Repositories/UnitOfWork.cs
index b7aa8c0..f9990de 100644
--- a/src/Supermarket.API/Persistence/Repositories/UnitOfWork.cs
+++ b/src/Supermarket.API/Persistence/Repositories/UnitOfWork.cs
@@ -6,10 +6,13 @@ namespace Supermarket.API.Persistence.Repositories
public class UnitOfWork(AppDbContext context) : IUnitOfWork
{
private readonly AppDbContext _context = context;
+ private readonly Lazy _productRepository= new Lazy(() => new ProductRepository(context));
+ private readonly Lazy _categoryRepository= new Lazy(() => new CategoryRepository(context));
- public async Task CompleteAsync()
- {
- await _context.SaveChangesAsync();
- }
+
+ public IProductRepository ProductRepository => _productRepository.Value;
+ public ICategoryRepository CategoryRepository => _categoryRepository.Value;
+
+ public async Task CompleteAsync() => await _context.SaveChangesAsync();
}
}
\ No newline at end of file
diff --git a/src/Supermarket.API/Properties/launchSettings.json b/src/Supermarket.API/Properties/launchSettings.json
index ce03645..b0cb87d 100644
--- a/src/Supermarket.API/Properties/launchSettings.json
+++ b/src/Supermarket.API/Properties/launchSettings.json
@@ -29,5 +29,13 @@
"httpPort": 5000
}
},
- "$schema": "http://json.schemastore.org/launchsettings.json"
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:62953/",
+ "sslPort": 44321
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Supermarket.API/Resources/ErrorResource.cs b/src/Supermarket.API/Resources/ErrorResource.cs
index 70cc737..90d7ac8 100644
--- a/src/Supermarket.API/Resources/ErrorResource.cs
+++ b/src/Supermarket.API/Resources/ErrorResource.cs
@@ -5,10 +5,7 @@ public record ErrorResource
public bool Success => false;
public List Messages { get; private set; }
- public ErrorResource(List messages)
- {
- Messages = messages ?? [];
- }
+ public ErrorResource(List messages) => Messages = messages ?? [];
public ErrorResource(string message)
{
diff --git a/src/Supermarket.API/Services/CategoryService.cs b/src/Supermarket.API/Services/CategoryService.cs
index 90126a1..77303c8 100644
--- a/src/Supermarket.API/Services/CategoryService.cs
+++ b/src/Supermarket.API/Services/CategoryService.cs
@@ -6,35 +6,24 @@
namespace Supermarket.API.Services
{
- public class CategoryService : ICategoryService
+ public class CategoryService(
+ IUnitOfWork unitOfWork,
+ IMemoryCache cache,
+ ILogger logger
+ ) : ICategoryService
{
- private readonly ICategoryRepository _categoryRepository;
- private readonly IUnitOfWork _unitOfWork;
- private readonly IMemoryCache _cache;
- private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork = unitOfWork;
+ private readonly IMemoryCache _cache = cache;
+ private readonly ILogger _logger = logger;
- public CategoryService
- (
- ICategoryRepository categoryRepository,
- IUnitOfWork unitOfWork,
- IMemoryCache cache,
- ILogger logger
- )
- {
- _categoryRepository = categoryRepository;
- _unitOfWork = unitOfWork;
- _cache = cache;
- _logger = logger;
- }
-
- public async Task> ListAsync()
+ public async Task> ListAsync()
{
// Here I try to get the categories list from the memory cache. If there is no data in cache, the anonymous method will be
// called, setting the cache to expire one minute ahead and returning the Task that lists the categories from the repository.
var categories = await _cache.GetOrCreateAsync(CacheKeys.CategoriesList, (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
- return _categoryRepository.ListAsync();
+ return _unitOfWork.CategoryRepository.ListAsync();
});
return categories ?? new List();
@@ -44,7 +33,7 @@ public async Task> SaveAsync(Category category)
{
try
{
- await _categoryRepository.AddAsync(category);
+ await _unitOfWork.CategoryRepository.AddAsync(category);
await _unitOfWork.CompleteAsync();
return new Response(category);
@@ -58,7 +47,7 @@ public async Task> SaveAsync(Category category)
public async Task> UpdateAsync(int id, Category category)
{
- var existingCategory = await _categoryRepository.FindByIdAsync(id);
+ var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(id);
if (existingCategory == null)
{
return new Response("Category not found.");
@@ -80,7 +69,7 @@ public async Task> UpdateAsync(int id, Category category)
public async Task> DeleteAsync(int id)
{
- var existingCategory = await _categoryRepository.FindByIdAsync(id);
+ var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(id);
if (existingCategory == null)
{
return new Response("Category not found.");
@@ -88,7 +77,7 @@ public async Task> DeleteAsync(int id)
try
{
- _categoryRepository.Remove(existingCategory);
+ _unitOfWork.CategoryRepository.Remove(existingCategory);
await _unitOfWork.CompleteAsync();
return new Response(existingCategory);
diff --git a/src/Supermarket.API/Services/ProductService.cs b/src/Supermarket.API/Services/ProductService.cs
index 3d02734..55232f6 100644
--- a/src/Supermarket.API/Services/ProductService.cs
+++ b/src/Supermarket.API/Services/ProductService.cs
@@ -6,29 +6,16 @@
namespace Supermarket.API.Services
{
- public class ProductService : IProductService
+ public class ProductService(
+ IUnitOfWork unitOfWork,
+ IMemoryCache cache,
+ ILogger logger
+ ) : IProductService
{
- private readonly IProductRepository _productRepository;
- private readonly ICategoryRepository _categoryRepository;
- private readonly IUnitOfWork _unitOfWork;
- private readonly IMemoryCache _cache;
- private readonly ILogger _logger;
-
- public ProductService
- (
- IProductRepository productRepository,
- ICategoryRepository categoryRepository,
- IUnitOfWork unitOfWork,
- IMemoryCache cache,
- ILogger logger
- )
- {
- _productRepository = productRepository;
- _categoryRepository = categoryRepository;
- _unitOfWork = unitOfWork;
- _cache = cache;
- _logger = logger;
- }
+
+ private readonly IUnitOfWork _unitOfWork = unitOfWork;
+ private readonly IMemoryCache _cache = cache;
+ private readonly ILogger _logger = logger;
public async Task> ListAsync(ProductsQuery query)
{
@@ -39,7 +26,7 @@ public async Task> ListAsync(ProductsQuery query)
var products = await _cache.GetOrCreateAsync(cacheKey, (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
- return _productRepository.ListAsync(query);
+ return _unitOfWork.ProductRepository.ListAsync(query);
});
return products!;
@@ -54,11 +41,11 @@ public async Task> SaveAsync(Product product)
You can create a method into the CategoryService class to return the category and inject the service here if you prefer, but
it doesn't matter given the API scope.
*/
- var existingCategory = await _categoryRepository.FindByIdAsync(product.CategoryId);
+ var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(product.CategoryId);
if (existingCategory == null)
return new Response("Invalid category.");
- await _productRepository.AddAsync(product);
+ await _unitOfWork.ProductRepository.AddAsync(product);
await _unitOfWork.CompleteAsync();
return new Response(product);
@@ -72,12 +59,12 @@ it doesn't matter given the API scope.
public async Task> UpdateAsync(int id, Product product)
{
- var existingProduct = await _productRepository.FindByIdAsync(id);
+ var existingProduct = await _unitOfWork.ProductRepository.FindByIdAsync(id);
if (existingProduct == null)
return new Response("Product not found.");
- var existingCategory = await _categoryRepository.FindByIdAsync(product.CategoryId);
+ var existingCategory = await _unitOfWork.CategoryRepository.FindByIdAsync(product.CategoryId);
if (existingCategory == null)
return new Response("Invalid category.");
@@ -88,7 +75,7 @@ public async Task> UpdateAsync(int id, Product product)
try
{
- _productRepository.Update(existingProduct);
+ _unitOfWork.ProductRepository.Update(existingProduct);
await _unitOfWork.CompleteAsync();
return new Response(existingProduct);
@@ -102,14 +89,14 @@ public async Task> UpdateAsync(int id, Product product)
public async Task> DeleteAsync(int id)
{
- var existingProduct = await _productRepository.FindByIdAsync(id);
+ var existingProduct = await _unitOfWork.ProductRepository.FindByIdAsync(id);
if (existingProduct == null)
return new Response("Product not found.");
try
{
- _productRepository.Remove(existingProduct);
+ _unitOfWork.ProductRepository.Remove(existingProduct);
await _unitOfWork.CompleteAsync();
return new Response(existingProduct);
diff --git a/src/Supermarket.API/Services/ServiceManager.cs b/src/Supermarket.API/Services/ServiceManager.cs
new file mode 100644
index 0000000..37b7989
--- /dev/null
+++ b/src/Supermarket.API/Services/ServiceManager.cs
@@ -0,0 +1,25 @@
+using Microsoft.Extensions.Caching.Memory;
+using Supermarket.API.Domain.Repositories;
+using Supermarket.API.Domain.Services;
+
+namespace Supermarket.API.Services
+{
+ public class ServiceManager(
+ IUnitOfWork unitOfWork,
+ IMemoryCache cache,
+ ILogger loggerProduct,
+ ILogger loggerCategory) : IServiceManager
+ {
+ private readonly IUnitOfWork _unitOfWork= unitOfWork;
+ private readonly IMemoryCache _cache= cache;
+ private readonly ILogger _logger=loggerProduct;
+ private readonly ILogger _loggerCategory=loggerCategory;
+
+ private readonly Lazy _productService = new Lazy(() => new ProductService(unitOfWork, cache, loggerProduct));
+ private readonly Lazy _categoryService= new Lazy(() => new CategoryService(unitOfWork, cache,loggerCategory));
+
+ public IProductService ProductService => _productService.Value;
+
+ public ICategoryService CategoryService => _categoryService.Value;
+ }
+}
diff --git a/src/Supermarket.API/Startup.cs b/src/Supermarket.API/Startup.cs
index 4848a62..9d87edf 100644
--- a/src/Supermarket.API/Startup.cs
+++ b/src/Supermarket.API/Startup.cs
@@ -14,16 +14,16 @@ public class Startup
private readonly IConfiguration Configuration;
public Startup(IConfiguration configuration) => Configuration = configuration;
-
+
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddCustomSwagger();
- services.Configure(options => options.LowercaseUrls = true);
+ services.Configure(options => options.LowercaseUrls = true);
- services.AddControllers().ConfigureApiBehaviorOptions(options =>
+ services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
// Adds a custom error response factory when ModelState is invalid
options.InvalidModelStateResponseFactory = InvalidModelStateResponseFactory.ProduceErrorResponse;
@@ -34,12 +34,10 @@ public void ConfigureServices(IServiceCollection services)
options.UseInMemoryDatabase(Configuration.GetConnectionString("memory") ?? "data-in-memory");
});
- services.AddScoped();
- services.AddScoped();
+
services.AddScoped();
- services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
services.AddAutoMapper(typeof(Startup));
}