Skip to content

Commit

Permalink
Basket : Improve, reduce, remove duplication call to database (dotnet…
Browse files Browse the repository at this point in the history
  • Loading branch information
michelcedric authored Nov 1, 2021
1 parent 47f69eb commit 984740d
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 79 deletions.
12 changes: 0 additions & 12 deletions src/ApplicationCore/Exceptions/BasketNotFoundException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,5 @@ public class BasketNotFoundException : Exception
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)
{
}
}
}
7 changes: 4 additions & 3 deletions src/ApplicationCore/Interfaces/IBasketService.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System.Collections.Generic;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using System.Collections.Generic;
using System.Threading.Tasks;

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);
}
}
15 changes: 11 additions & 4 deletions src/ApplicationCore/Services/BasketService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@ public BasketService(IRepository<Basket> basketRepository,
_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)
Expand All @@ -36,7 +42,7 @@ public async Task DeleteBasketAsync(int basketId)
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);
Expand All @@ -53,6 +59,7 @@ public async Task SetQuantities(int basketId, Dictionary<string, int> quantities
}
basket.RemoveEmptyItems();
await _basketRepository.UpdateAsync(basket);
return basket;
}

public async Task TransferBasketAsync(string anonymousId, string userName)
Expand Down
7 changes: 6 additions & 1 deletion src/Web/Interfaces/IBasketViewModelService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
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
{
public interface IBasketViewModelService
{
Task<BasketViewModel> GetOrCreateBasketForUser(string userName);

Task<int> CountTotalBasketItems(string username);

Task<BasketViewModel> Map(Basket basket);
}
}
56 changes: 22 additions & 34 deletions src/Web/Pages/Basket/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,24 +14,20 @@ 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;
}

public BasketViewModel BasketModel { get; set; } = new BasketViewModel();

public async Task OnGet()
{
await SetBasketModelAsync();
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName());
}

public async Task<IActionResult> OnPost(CatalogItemViewModel productDetails)
Expand All @@ -42,64 +36,58 @@ public async Task<IActionResult> OnPost(CatalogItemViewModel productDetails)
{
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;
}
}
}
18 changes: 9 additions & 9 deletions src/Web/Pages/Shared/Components/BasketComponent/Basket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -22,25 +20,27 @@ public Basket(IBasketViewModelService basketService,
_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()
Expand Down
34 changes: 21 additions & 13 deletions src/Web/Services/BasketViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,9 @@ public async Task<BasketViewModel> GetOrCreateBasketForUser(string userName)
{
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;
}
}

private async Task<BasketViewModel> CreateBasketForUser(string userId)
{
Expand Down Expand Up @@ -84,5 +73,24 @@ private async Task<List<BasketItemViewModel>> GetBasketItems(IReadOnlyCollection

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);
}
}
}
2 changes: 1 addition & 1 deletion src/Web/Views/Shared/_LoginPartial.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public async Task InvokesBasketRepositoryGetBySpecAsyncOnce()

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);
}
Expand All @@ -36,7 +36,7 @@ public async Task InvokesBasketRepositoryUpdateAsyncOnce()

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);
}
Expand Down

0 comments on commit 984740d

Please sign in to comment.