Basket : Improve, reduce, remove duplication call to database (#610)
This commit is contained in:
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user