diff --git a/src/ApplicationCore/Interfaces/IBasketQueryService.cs b/src/ApplicationCore/Interfaces/IBasketQueryService.cs new file mode 100644 index 0000000..6136df7 --- /dev/null +++ b/src/ApplicationCore/Interfaces/IBasketQueryService.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +/// +/// Specific query used to fetch count without running in memory +/// +public interface IBasketQueryService +{ + Task CountTotalBasketItems(string username); +} + diff --git a/src/Infrastructure/Data/Queries/BasketQueryService.cs b/src/Infrastructure/Data/Queries/BasketQueryService.cs new file mode 100644 index 0000000..9f6c4d9 --- /dev/null +++ b/src/Infrastructure/Data/Queries/BasketQueryService.cs @@ -0,0 +1,31 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; + +namespace Microsoft.eShopWeb.Infrastructure.Data.Queries; + +public class BasketQueryService : IBasketQueryService +{ + private readonly CatalogContext _dbContext; + + public BasketQueryService(CatalogContext dbContext) + { + _dbContext = dbContext; + } + + /// + /// This method performs the sum on the database rather than in memory + /// + /// + /// + public async Task CountTotalBasketItems(string username) + { + var totalItems = await _dbContext.Baskets + .Where(basket => basket.BuyerId == username) + .SelectMany(item => item.Items) + .SumAsync(sum => sum.Quantity); + + return totalItems; + } +} diff --git a/src/Web/Configuration/ConfigureCoreServices.cs b/src/Web/Configuration/ConfigureCoreServices.cs index b9b843f..f38657d 100644 --- a/src/Web/Configuration/ConfigureCoreServices.cs +++ b/src/Web/Configuration/ConfigureCoreServices.cs @@ -1,6 +1,7 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Microsoft.eShopWeb.Infrastructure.Data; +using Microsoft.eShopWeb.Infrastructure.Data.Queries; using Microsoft.eShopWeb.Infrastructure.Logging; using Microsoft.eShopWeb.Infrastructure.Services; using Microsoft.Extensions.Configuration; @@ -18,6 +19,7 @@ public static class ConfigureCoreServices services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddSingleton(new UriComposer(configuration.Get())); services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); services.AddTransient(); diff --git a/src/Web/Interfaces/IBasketViewModelService.cs b/src/Web/Interfaces/IBasketViewModelService.cs index a5cb8b4..85d1c87 100644 --- a/src/Web/Interfaces/IBasketViewModelService.cs +++ b/src/Web/Interfaces/IBasketViewModelService.cs @@ -7,5 +7,6 @@ namespace Microsoft.eShopWeb.Web.Interfaces; public interface IBasketViewModelService { Task GetOrCreateBasketForUser(string userName); + Task CountTotalBasketItems(string username); Task Map(Basket basket); } diff --git a/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs b/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs index 4b6c6c6..519a335 100644 --- a/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs +++ b/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs @@ -2,27 +2,21 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.ViewModels; -using BasketEntity = Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate.Basket; namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent; public class Basket : ViewComponent { - private readonly IBasketViewModelService _basketViewModelService; - private readonly IReadRepository _basketRepository; + private readonly IBasketViewModelService _basketService; private readonly SignInManager _signInManager; - public Basket(IBasketViewModelService basketViewModelService, - IReadRepository basketRepository, - SignInManager signInManager) + public Basket(IBasketViewModelService basketService, + SignInManager signInManager) { - _basketViewModelService = basketViewModelService; - _basketRepository = basketRepository; + _basketService = basketService; _signInManager = signInManager; } @@ -39,26 +33,18 @@ public class Basket : ViewComponent { if (_signInManager.IsSignedIn(HttpContext.User)) { - var userNameSpec = new BasketWithItemsSpecification(User.Identity.Name); - - var userBasket = await _basketRepository.GetBySpecAsync(userNameSpec); - if (userBasket == null) return 0; - return userBasket.TotalItems; + return await _basketService.CountTotalBasketItems(User.Identity.Name); } string anonymousId = GetAnnonymousIdFromCookie(); if (anonymousId == null) return 0; - var anonymousSpec = new BasketWithItemsSpecification(anonymousId); - var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousSpec); - if (anonymousBasket == null) return 0; - return anonymousBasket.TotalItems; + return await _basketService.CountTotalBasketItems(anonymousId); } private string GetAnnonymousIdFromCookie() { - // TODO: Add a prefix to anonymous cookie values so they cannot collide with user names if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) { var id = Request.Cookies[Constants.BASKET_COOKIENAME]; diff --git a/src/Web/Services/BasketViewModelService.cs b/src/Web/Services/BasketViewModelService.cs index ab12285..06cfdf4 100644 --- a/src/Web/Services/BasketViewModelService.cs +++ b/src/Web/Services/BasketViewModelService.cs @@ -14,14 +14,17 @@ public class BasketViewModelService : IBasketViewModelService { private readonly IRepository _basketRepository; private readonly IUriComposer _uriComposer; - private readonly IReadRepository _itemRepository; + private readonly IBasketQueryService _basketQueryService; + private readonly IRepository _itemRepository; public BasketViewModelService(IRepository basketRepository, - IReadRepository itemRepository, - IUriComposer uriComposer) + IRepository itemRepository, + IUriComposer uriComposer, + IBasketQueryService basketQueryService) { _basketRepository = basketRepository; _uriComposer = uriComposer; + _basketQueryService = basketQueryService; _itemRepository = itemRepository; } @@ -83,4 +86,11 @@ public class BasketViewModelService : IBasketViewModelService Items = await GetBasketItems(basket.Items) }; } + + public async Task CountTotalBasketItems(string username) + { + var counter = await _basketQueryService.CountTotalBasketItems(username); + + return counter; + } }