Commit 52dce923 authored by Michal Bilanin's avatar Michal Bilanin 🙄
Browse files

Merge branch 'feat/fix-caching' into 'milestone-3'

Feat/fix caching

See merge request !33
parents e36c26a7 07176f87
Loading
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@ using Commons.Enums;
using JuiceWorld.Entities;
using JuiceWorld.Entities;
using JuiceWorld.QueryObjects;
using JuiceWorld.QueryObjects;
using JuiceWorld.Repositories;
using JuiceWorld.Repositories;
using JuiceWorld.UnitOfWork;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using TestUtilities.MockedObjects;
using TestUtilities.MockedObjects;
@@ -28,7 +29,8 @@ public class ProductServiceTests
        var mapper = config.CreateMapper();
        var mapper = config.CreateMapper();
        var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
        var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
        var cache = new MemoryCache(new MemoryCacheOptions());
        var cache = new MemoryCache(new MemoryCacheOptions());
        _productService = new ProductService(productRepository, mapper, cache, new QueryObject<Product>(dbContext));
        var unitOfwork = new ProductUnitOfWork(dbContext);
        _productService = new ProductService(productRepository, mapper, cache, new QueryObject<Product>(dbContext), unitOfwork);
    }
    }


    [Fact]
    [Fact]
+6 −31
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@ using Commons.Enums;
using Infrastructure.QueryObjects;
using Infrastructure.QueryObjects;
using Infrastructure.Repositories;
using Infrastructure.Repositories;
using JuiceWorld.Entities;
using JuiceWorld.Entities;
using JuiceWorld.UnitOfWork;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Memory;
using Moq;
using Moq;
using Xunit;
using Xunit;
@@ -18,7 +19,9 @@ namespace BusinessLayer.Tests.Stubs
    {
    {
        private readonly IProductService _productService;
        private readonly IProductService _productService;
        private readonly Mock<IRepository<Product>> _productRepositoryMock;
        private readonly Mock<IRepository<Product>> _productRepositoryMock;
        private readonly Mock<IRepository<Tag>> _tagRepositoryMock;
        private readonly IMapper _mapper;
        private readonly IMapper _mapper;
        private readonly Mock<ProductUnitOfWork> _unitofWork;


        private readonly List<Product> _products = new List<Product>
        private readonly List<Product> _products = new List<Product>
        {
        {
@@ -53,7 +56,9 @@ namespace BusinessLayer.Tests.Stubs
            _mapper = config.CreateMapper();
            _mapper = config.CreateMapper();
            var cache = new MemoryCache(new MemoryCacheOptions());
            var cache = new MemoryCache(new MemoryCacheOptions());
            // Initialize the service
            // Initialize the service
            _productService = new ProductService(_productRepositoryMock.Object, _mapper, cache, queryObjectMock.Object);
            _tagRepositoryMock = new Mock<IRepository<Tag>>();
            _unitofWork = new Mock<ProductUnitOfWork>(_productRepositoryMock.Object, _tagRepositoryMock.Object);
            _productService = new ProductService(_productRepositoryMock.Object, _mapper, cache, queryObjectMock.Object, _unitofWork.Object);
        }
        }


        [Fact]
        [Fact]
@@ -116,36 +121,6 @@ namespace BusinessLayer.Tests.Stubs
            Assert.Equal(productDto.ManufacturerId, result.ManufacturerId);
            Assert.Equal(productDto.ManufacturerId, result.ManufacturerId);
        }
        }


        [Fact]
        public async Task UpdateProductAsync_Simple()
        {
            // Arrange
            var productDto = new ProductDto
            {
                Id = 1,
                Name = "Updated Product",
                Price = 120m,
                Description = "Updated Description",
                Category = ProductCategory.Testosterone,
                ManufacturerId = 1
            };

            var updatedProduct = _mapper.Map<Product>(productDto);
            _productRepositoryMock.Setup(repo => repo.GetByIdAsync(productDto.Id)).ReturnsAsync(_products[0]);
            _productRepositoryMock.Setup(repo => repo.UpdateAsync(It.IsAny<Product>(), null)).ReturnsAsync(updatedProduct);

            // Act
            var result = await _productService.UpdateProductAsync(productDto);

            // Assert
            Assert.NotNull(result);
            Assert.Equal(productDto.Name, result.Name);
            Assert.Equal(productDto.Price, result.Price);
            Assert.Equal(productDto.Description, result.Description);
            Assert.Equal(productDto.Category, result.Category);
            Assert.Equal(productDto.ManufacturerId, result.ManufacturerId);
        }

        [Fact]
        [Fact]
        public async Task DeleteProductByIdAsync_Simple()
        public async Task DeleteProductByIdAsync_Simple()
        {
        {
+6 −13
Original line number Original line Diff line number Diff line
@@ -38,9 +38,6 @@ public class ManufacturerService(


    public async Task<FilteredResult<ManufacturerDto>> GetManufacturersAsync(
    public async Task<FilteredResult<ManufacturerDto>> GetManufacturersAsync(
        ManufacturerFilterDto manufacturerFilterDto)
        ManufacturerFilterDto manufacturerFilterDto)
    {
        string cacheKey = $"{_cacheKeyPrefix}-manufacturers{JsonSerializer.Serialize(manufacturerFilterDto)}";
        if (!memoryCache.TryGetValue(cacheKey, out FilteredResult<Manufacturer>? value))
    {
    {
        var query = manufacturerQueryObject
        var query = manufacturerQueryObject
            .Filter(m => manufacturerFilterDto.Name == null ||
            .Filter(m => manufacturerFilterDto.Name == null ||
@@ -48,12 +45,8 @@ public class ManufacturerService(
            .Paginate(manufacturerFilterDto.PageIndex, manufacturerFilterDto.PageSize)
            .Paginate(manufacturerFilterDto.PageIndex, manufacturerFilterDto.PageSize)
            .OrderBy(m => m.Id);
            .OrderBy(m => m.Id);


            value = await query.ExecuteAsync();
        var value = await query.ExecuteAsync();


            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromSeconds(30));
            memoryCache.Set(cacheKey, value, cacheEntryOptions);
        }


        return new FilteredResult<ManufacturerDto>
        return new FilteredResult<ManufacturerDto>
        {
        {
+31 −21
Original line number Original line Diff line number Diff line
@@ -104,10 +104,6 @@ public class OrderService(


    public async Task<FilteredResult<OrderDto>> GetOrdersByUserIdAsync(int userId, PaginationDto paginationDto)
    public async Task<FilteredResult<OrderDto>> GetOrdersByUserIdAsync(int userId, PaginationDto paginationDto)
    {
    {
        string cacheKey = $"{_cacheKeyPrefix}-Orders{userId}{JsonSerializer.Serialize(paginationDto)}";
        if (!memoryCache.TryGetValue(cacheKey, out FilteredResult<Order>? value))
        {

        var query = orderQueryObject.Filter(o => o.UserId == userId)
        var query = orderQueryObject.Filter(o => o.UserId == userId)
                .OrderBy(
                .OrderBy(
                    new (Expression<Func<Order, object>> KeySelector, bool IsDesc)[]
                    new (Expression<Func<Order, object>> KeySelector, bool IsDesc)[]
@@ -117,12 +113,7 @@ public class OrderService(
                    })
                    })
            .Paginate(paginationDto.PageIndex, paginationDto.PageSize);
            .Paginate(paginationDto.PageIndex, paginationDto.PageSize);


            value = await query.ExecuteAsync();
        var value = await query.ExecuteAsync();

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromSeconds(30));
            memoryCache.Set(cacheKey, value, cacheEntryOptions);
        }


        return new FilteredResult<OrderDto>
        return new FilteredResult<OrderDto>
        {
        {
@@ -140,19 +131,36 @@ public class OrderService(


    public async Task<OrderDetailDto?> GetOrderDetailByIdAsync(int id)
    public async Task<OrderDetailDto?> GetOrderDetailByIdAsync(int id)
    {
    {
        var order = await orderRepository.GetByIdAsync(id,
        string cacheKey = $"{_cacheKeyPrefix}-OrderDetail{id}";
            nameof(Order.OrderProducts), $"{nameof(Order.OrderProducts)}.{nameof(OrderProduct.Product)}", nameof(User));
        if (!memoryCache.TryGetValue(cacheKey, out Order? value))
        return order is null ? null : mapper.Map<OrderDetailDto>(order);
        {
            value = await orderRepository.GetByIdAsync(id,
                nameof(Order.OrderProducts), $"{nameof(Order.OrderProducts)}.{nameof(OrderProduct.Product)}",
                nameof(User));

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromSeconds(30));
            memoryCache.Set(cacheKey, value, cacheEntryOptions);
        }

        return value is null ? null : mapper.Map<OrderDetailDto>(value);
    }
    }


    public async Task<OrderDto?> UpdateOrderAsync(OrderDto orderDto)
    public async Task<OrderDto?> UpdateOrderAsync(OrderDto orderDto)
    {
    {
        string cacheKey = $"{_cacheKeyPrefix}-OrderDetail{orderDto.Id}";
        memoryCache.Remove(cacheKey);


        var updatedOrder = await orderRepository.UpdateAsync(mapper.Map<Order>(orderDto));
        var updatedOrder = await orderRepository.UpdateAsync(mapper.Map<Order>(orderDto));
        return updatedOrder is null ? null : mapper.Map<OrderDto>(updatedOrder);
        return updatedOrder is null ? null : mapper.Map<OrderDto>(updatedOrder);
    }
    }


    public async Task<OrderDto?> UpdateOrderAsync(OrderDetailDto orderDto)
    public async Task<OrderDto?> UpdateOrderAsync(OrderDetailDto orderDto)
    {
    {
        string cacheKey = $"{_cacheKeyPrefix}-OrderDetail{orderDto.Id}";
        memoryCache.Remove(cacheKey);

        var order = mapper.Map<Order>(orderDto);
        var order = mapper.Map<Order>(orderDto);
        order.OrderProducts = [];
        order.OrderProducts = [];
        var updatedOrder = await orderUnitOfWork.OrderRepository.UpdateAsync(order);
        var updatedOrder = await orderUnitOfWork.OrderRepository.UpdateAsync(order);
@@ -199,6 +207,8 @@ public class OrderService(


    public Task<bool> DeleteOrderByIdAsync(int id)
    public Task<bool> DeleteOrderByIdAsync(int id)
    {
    {
        string cacheKey = $"{_cacheKeyPrefix}-OrderDetail{id}";
        memoryCache.Remove(cacheKey);
        return orderRepository.DeleteAsync(id);
        return orderRepository.DeleteAsync(id);
    }
    }
}
}
+21 −5
Original line number Original line Diff line number Diff line
@@ -6,6 +6,7 @@ using Commons.Enums;
using Infrastructure.QueryObjects;
using Infrastructure.QueryObjects;
using Infrastructure.Repositories;
using Infrastructure.Repositories;
using JuiceWorld.Entities;
using JuiceWorld.Entities;
using JuiceWorld.UnitOfWork;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Memory;


namespace BusinessLayer.Services;
namespace BusinessLayer.Services;
@@ -14,7 +15,9 @@ public class ProductService(
    IRepository<Product> productRepository,
    IRepository<Product> productRepository,
    IMapper mapper,
    IMapper mapper,
    IMemoryCache memoryCache,
    IMemoryCache memoryCache,
    IQueryObject<Product> queryObject) : IProductService
    IQueryObject<Product> queryObject,
    ProductUnitOfWork productUnitOfWork
    ) : IProductService
{
{
    private string _cacheKeyPrefix = nameof(ProductService);
    private string _cacheKeyPrefix = nameof(ProductService);
    public async Task<ProductDto?> CreateProductAsync(ProductDto productDto)
    public async Task<ProductDto?> CreateProductAsync(ProductDto productDto)
@@ -127,20 +130,33 @@ public class ProductService(


    public async Task<ProductDto?> UpdateProductAsync(ProductDto productDto)
    public async Task<ProductDto?> UpdateProductAsync(ProductDto productDto)
    {
    {
        var oldProduct = await productRepository.GetByIdAsync(productDto.Id);
        string cacheKeyDetail = $"{_cacheKeyPrefix}-productDetail{productDto.Id}";
        memoryCache.Remove(cacheKeyDetail);
        string cacheKey1 = $"{_cacheKeyPrefix}-product{productDto.Id}";
        memoryCache.Remove(cacheKey1);
        var oldProduct = await productUnitOfWork.ProductRepository.GetByIdAsync(productDto.Id);
        if (oldProduct is null)
        if (oldProduct is null)
        {
        {
            return null;
            return null;
        }
        }


        var updatedProduct = await productRepository.UpdateAsync(mapper.Map<Product>(productDto));
        var product = mapper.Map<Product>(productDto);
        oldProduct.Tags.Clear();

        var tags = await productUnitOfWork.TagRepository.GetByIdRangeAsync(productDto.TagIds.Cast<object>());
        product.Tags = tags.ToList();

        var updatedProduct = await productUnitOfWork.ProductRepository.UpdateAsync(product);
        await productUnitOfWork.Commit();
        return updatedProduct is null ? null : mapper.Map<ProductDto>(updatedProduct);
        return updatedProduct is null ? null : mapper.Map<ProductDto>(updatedProduct);
    }
    }


    public async Task<bool> DeleteProductByIdAsync(int id)
    public async Task<bool> DeleteProductByIdAsync(int id)
    {
    {
        string cacheKey = $"{_cacheKeyPrefix}-productDetail{id}";
        string cacheKeyDetail = $"{_cacheKeyPrefix}-productDetail{id}";
        memoryCache.Remove(cacheKey);
        memoryCache.Remove(cacheKeyDetail);
        string cacheKey1 = $"{_cacheKeyPrefix}-product{id}";
        memoryCache.Remove(cacheKey1);
        return await productRepository.DeleteAsync(id);
        return await productRepository.DeleteAsync(id);
    }
    }
}
}
Loading