Basket : Improve, reduce, remove duplication call to database (#610)

This commit is contained in:
Cédric Michel
2021-11-01 22:09:34 +01:00
committed by GitHub
parent 47f69eb294
commit 984740d422
9 changed files with 76 additions and 79 deletions

View File

@@ -7,17 +7,5 @@ namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
{
}
protected BasketNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
}
public BasketNotFoundException(string message) : base(message)
{
}
public BasketNotFoundException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
@@ -6,8 +7,8 @@ namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
public interface IBasketService
{
Task TransferBasketAsync(string anonymousId, string userName);
Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity = 1);
Task SetQuantities(int basketId, Dictionary<string, int> quantities);
Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1);
Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities);
Task DeleteBasketAsync(int basketId);
}
}

View File

@@ -19,15 +19,21 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
_logger = logger;
}
public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity = 1)
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
{
var basketSpec = new BasketWithItemsSpecification(basketId);
var basketSpec = new BasketWithItemsSpecification(username);
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
Guard.Against.NullBasket(basketId, basket);
if (basket == null)
{
basket = new Basket(username);
await _basketRepository.AddAsync(basket);
}
basket.AddItem(catalogItemId, price, quantity);
await _basketRepository.UpdateAsync(basket);
return basket;
}
public async Task DeleteBasketAsync(int basketId)
@@ -36,7 +42,7 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
await _basketRepository.DeleteAsync(basket);
}
public async Task SetQuantities(int basketId, Dictionary<string, int> quantities)
public async Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities)
{
Guard.Against.Null(quantities, nameof(quantities));
var basketSpec = new BasketWithItemsSpecification(basketId);
@@ -53,6 +59,7 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
}
basket.RemoveEmptyItems();
await _basketRepository.UpdateAsync(basket);
return basket;
}
public async Task TransferBasketAsync(string anonymousId, string userName)

View File

@@ -1,4 +1,5 @@
using Microsoft.eShopWeb.Web.Pages.Basket;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.Web.Pages.Basket;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Interfaces
@@ -6,5 +7,9 @@ namespace Microsoft.eShopWeb.Web.Interfaces
public interface IBasketViewModelService
{
Task<BasketViewModel> GetOrCreateBasketForUser(string userName);
Task<int> CountTotalBasketItems(string username);
Task<BasketViewModel> Map(Basket basket);
}
}

View File

@@ -1,9 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.ViewModels;
using System;
@@ -16,16 +14,12 @@ namespace Microsoft.eShopWeb.Web.Pages.Basket
public class IndexModel : PageModel
{
private readonly IBasketService _basketService;
private readonly SignInManager<ApplicationUser> _signInManager;
private string _username = null;
private readonly IBasketViewModelService _basketViewModelService;
public IndexModel(IBasketService basketService,
IBasketViewModelService basketViewModelService,
SignInManager<ApplicationUser> signInManager)
IBasketViewModelService basketViewModelService)
{
_basketService = basketService;
_signInManager = signInManager;
_basketViewModelService = basketViewModelService;
}
@@ -33,7 +27,7 @@ namespace Microsoft.eShopWeb.Web.Pages.Basket
public async Task OnGet()
{
await SetBasketModelAsync();
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName());
}
public async Task<IActionResult> OnPost(CatalogItemViewModel productDetails)
@@ -42,64 +36,58 @@ namespace Microsoft.eShopWeb.Web.Pages.Basket
{
return RedirectToPage("/Index");
}
await SetBasketModelAsync();
await _basketService.AddItemToBasket(BasketModel.Id, productDetails.Id, productDetails.Price);
var username = GetOrSetBasketCookieAndUserName();
var basket = await _basketService.AddItemToBasket(username,
productDetails.Id, productDetails.Price);
await SetBasketModelAsync();
BasketModel = await _basketViewModelService.Map(basket);
return RedirectToPage();
}
public async Task OnPostUpdate(IEnumerable<BasketItemViewModel> items)
{
await SetBasketModelAsync();
if (!ModelState.IsValid)
{
return;
}
var basketView = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName());
var updateModel = items.ToDictionary(b => b.Id.ToString(), b => b.Quantity);
await _basketService.SetQuantities(BasketModel.Id, updateModel);
await SetBasketModelAsync();
var basket = await _basketService.SetQuantities(basketView.Id, updateModel);
BasketModel = await _basketViewModelService.Map(basket);
}
private async Task SetBasketModelAsync()
private string GetOrSetBasketCookieAndUserName()
{
if (_signInManager.IsSignedIn(HttpContext.User))
{
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
}
else
{
GetOrSetBasketCookieAndUserName();
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username);
string userName = null;
}
if (Request.HttpContext.User.Identity.IsAuthenticated)
{
return Request.HttpContext.User.Identity.Name;
}
private void GetOrSetBasketCookieAndUserName()
{
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
{
_username = Request.Cookies[Constants.BASKET_COOKIENAME];
userName = Request.Cookies[Constants.BASKET_COOKIENAME];
if (!Request.HttpContext.User.Identity.IsAuthenticated)
{
if (!Guid.TryParse(_username, out var _))
if (!Guid.TryParse(userName, out var _))
{
_username = null;
userName = null;
}
}
}
if (_username != null) return;
if (userName != null) return userName;
_username = Guid.NewGuid().ToString();
userName = Guid.NewGuid().ToString();
var cookieOptions = new CookieOptions { IsEssential = true };
cookieOptions.Expires = DateTime.Today.AddYears(10);
Response.Cookies.Append(Constants.BASKET_COOKIENAME, _username, cookieOptions);
Response.Cookies.Append(Constants.BASKET_COOKIENAME, userName, cookieOptions);
return userName;
}
}
}

View File

@@ -2,10 +2,8 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.Pages.Basket;
using Microsoft.eShopWeb.Web.ViewModels;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent
@@ -22,25 +20,27 @@ namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent
_signInManager = signInManager;
}
public async Task<IViewComponentResult> InvokeAsync(string userName)
public async Task<IViewComponentResult> InvokeAsync()
{
var vm = new BasketComponentViewModel();
vm.ItemsCount = (await GetBasketViewModelAsync()).Items.Sum(i => i.Quantity);
var vm = new BasketComponentViewModel
{
ItemsCount = await CountTotalBasketItems()
};
return View(vm);
}
private async Task<BasketViewModel> GetBasketViewModelAsync()
private async Task<int> CountTotalBasketItems()
{
if (_signInManager.IsSignedIn(HttpContext.User))
{
return await _basketService.GetOrCreateBasketForUser(User.Identity.Name);
return await _basketService.CountTotalBasketItems(User.Identity.Name);
}
string anonymousId = GetAnnonymousIdFromCookie();
if (anonymousId == null)
return new BasketViewModel();
return 0;
return await _basketService.GetOrCreateBasketForUser(anonymousId);
return await _basketService.CountTotalBasketItems(anonymousId);
}
private string GetAnnonymousIdFromCookie()

View File

@@ -34,18 +34,7 @@ namespace Microsoft.eShopWeb.Web.Services
{
return await CreateBasketForUser(userName);
}
return await CreateViewModelFromBasket(basket);
}
private async Task<BasketViewModel> CreateViewModelFromBasket(Basket basket)
{
var viewModel = new BasketViewModel
{
Id = basket.Id,
BuyerId = basket.BuyerId,
Items = await GetBasketItems(basket.Items)
};
var viewModel = await Map(basket);
return viewModel;
}
@@ -84,5 +73,24 @@ namespace Microsoft.eShopWeb.Web.Services
return items;
}
public async Task<BasketViewModel> Map(Basket basket)
{
return new BasketViewModel()
{
BuyerId = basket.BuyerId,
Id = basket.Id,
Items = await GetBasketItems(basket.Items)
};
}
public async Task<int> CountTotalBasketItems(string username)
{
var basketSpec = new BasketWithItemsSpecification(username);
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
if (basket == null)
return 0;
return basket.Items.Sum(i => i.Quantity);
}
}
}

View File

@@ -35,7 +35,7 @@
</section>
<section class="col-lg-1 col-xs-12">
@await Component.InvokeAsync("Basket", User.Identity.Name)
@await Component.InvokeAsync("Basket")
</section>
}

View File

@@ -22,7 +22,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTes
var basketService = new BasketService(_mockBasketRepo.Object, null);
await basketService.AddItemToBasket(basket.Id, 1, 1.50m);
await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m);
_mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny<BasketWithItemsSpecification>(), default), Times.Once);
}
@@ -36,7 +36,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTes
var basketService = new BasketService(_mockBasketRepo.Object, null);
await basketService.AddItemToBasket(basket.Id, 1, 1.50m);
await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m);
_mockBasketRepo.Verify(x => x.UpdateAsync(basket, default), Times.Once);
}