From 16d81ae4505650d1dc141d0eed9908c4b09cc91d Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Mon, 23 Oct 2017 10:52:33 -0400 Subject: [PATCH] Refactoring Services (#61) * Refactoring ViewModels into Razor Pages models * Cleaning up Basket viewcomponent * Refactoring services. Fixed bug in basket item counter. --- .../Interfaces/IBasketService.cs | 7 +- .../Interfaces/IOrderService.cs | 1 + .../Services/BasketService.cs | 89 ++++++------------- .../Services/OrderService.cs | 2 +- src/Infrastructure/Infrastructure.csproj | 1 + src/Web/Controllers/AccountController.cs | 3 +- src/Web/Controllers/BasketController.cs | 7 +- src/Web/Interfaces/IBasketService.cs | 7 +- src/Web/Services/BasketViewModelService.cs | 76 ++++++++++++++++ src/Web/Services/CatalogService.cs | 4 + src/Web/Startup.cs | 2 +- src/Web/ViewComponents/Basket.cs | 4 +- .../Interfaces/IBasketViewModelService.cs | 11 +++ .../Pages/Account/Register.cshtml.cs | 21 ++++- .../Pages/Account/Signin.cshtml.cs | 4 +- .../Pages/Basket/Index.cshtml.cs | 7 +- .../Pages/Components/Basket/Default.cshtml | 3 +- .../Pages/Order/Detail.cshtml.cs | 25 +++++- src/WebRazorPages/Pages/Order/Index.cshtml.cs | 23 +++-- src/WebRazorPages/Pages/_LoginPartial.cshtml | 2 +- ...etService.cs => BasketViewModelService.cs} | 52 ++--------- src/WebRazorPages/Services/CatalogService.cs | 4 + src/WebRazorPages/Startup.cs | 2 +- src/WebRazorPages/ViewComponents/Basket.cs | 24 ++--- .../ViewModels/BasketComponentViewModel.cs | 7 -- .../ViewModels/OrderItemViewModel.cs | 18 ---- .../ViewModels/OrderViewModel.cs | 18 ---- .../ViewModels/RegisterViewModel.cs | 24 ----- 28 files changed, 218 insertions(+), 230 deletions(-) rename src/{WebRazorPages => ApplicationCore}/Interfaces/IBasketService.cs (63%) rename src/{Web => ApplicationCore}/Services/BasketService.cs (58%) rename src/{Infrastructure => ApplicationCore}/Services/OrderService.cs (97%) create mode 100644 src/Web/Services/BasketViewModelService.cs create mode 100644 src/WebRazorPages/Interfaces/IBasketViewModelService.cs rename src/WebRazorPages/Services/{BasketService.cs => BasketViewModelService.cs} (55%) delete mode 100644 src/WebRazorPages/ViewModels/BasketComponentViewModel.cs delete mode 100644 src/WebRazorPages/ViewModels/OrderItemViewModel.cs delete mode 100644 src/WebRazorPages/ViewModels/OrderViewModel.cs delete mode 100644 src/WebRazorPages/ViewModels/RegisterViewModel.cs diff --git a/src/WebRazorPages/Interfaces/IBasketService.cs b/src/ApplicationCore/Interfaces/IBasketService.cs similarity index 63% rename from src/WebRazorPages/Interfaces/IBasketService.cs rename to src/ApplicationCore/Interfaces/IBasketService.cs index 50012a7..7be1a9d 100644 --- a/src/WebRazorPages/Interfaces/IBasketService.cs +++ b/src/ApplicationCore/Interfaces/IBasketService.cs @@ -1,12 +1,11 @@ -using Microsoft.eShopWeb.RazorPages.ViewModels; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; -namespace Microsoft.eShopWeb.RazorPages.Interfaces +namespace ApplicationCore.Interfaces { public interface IBasketService { - Task GetOrCreateBasketForUser(string userName); + Task GetBasketItemCountAsync(string userName); Task TransferBasketAsync(string anonymousId, string userName); Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity); Task SetQuantities(int basketId, Dictionary quantities); diff --git a/src/ApplicationCore/Interfaces/IOrderService.cs b/src/ApplicationCore/Interfaces/IOrderService.cs index 3c37ab1..414f86d 100644 --- a/src/ApplicationCore/Interfaces/IOrderService.cs +++ b/src/ApplicationCore/Interfaces/IOrderService.cs @@ -1,4 +1,5 @@ using ApplicationCore.Entities.OrderAggregate; +using System.Collections.Generic; using System.Threading.Tasks; namespace ApplicationCore.Interfaces diff --git a/src/Web/Services/BasketService.cs b/src/ApplicationCore/Services/BasketService.cs similarity index 58% rename from src/Web/Services/BasketService.cs rename to src/ApplicationCore/Services/BasketService.cs index 5a54f10..209886d 100644 --- a/src/Web/Services/BasketService.cs +++ b/src/ApplicationCore/Services/BasketService.cs @@ -1,13 +1,11 @@ using ApplicationCore.Interfaces; +using System.Threading.Tasks; +using System.Collections.Generic; using ApplicationCore.Specifications; using Microsoft.eShopWeb.ApplicationCore.Entities; -using Microsoft.eShopWeb.Interfaces; -using Microsoft.eShopWeb.ViewModels; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Services +namespace ApplicationCore.Services { public class BasketService : IBasketService { @@ -27,55 +25,6 @@ namespace Microsoft.eShopWeb.Services _itemRepository = itemRepository; } - public async Task GetOrCreateBasketForUser(string userName) - { - var basketSpec = new BasketWithItemsSpecification(userName); - var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); - - if(basket == null) - { - return await CreateBasketForUser(userName); - } - return CreateViewModelFromBasket(basket); - } - - private BasketViewModel CreateViewModelFromBasket(Basket basket) - { - var viewModel = new BasketViewModel(); - viewModel.Id = basket.Id; - viewModel.BuyerId = basket.BuyerId; - viewModel.Items = basket.Items.Select(i => - { - var itemModel = new BasketItemViewModel() - { - Id = i.Id, - UnitPrice = i.UnitPrice, - Quantity = i.Quantity, - CatalogItemId = i.CatalogItemId - - }; - var item = _itemRepository.GetById(i.CatalogItemId); - itemModel.PictureUrl = _uriComposer.ComposePicUri(item.PictureUri); - itemModel.ProductName = item.Name; - return itemModel; - }) - .ToList(); - return viewModel; - } - - public async Task CreateBasketForUser(string userId) - { - var basket = new Basket() { BuyerId = userId }; - await _basketRepository.AddAsync(basket); - - return new BasketViewModel() - { - BuyerId = basket.BuyerId, - Id = basket.Id, - Items = new List() - }; - } - public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity) { var basket = await _basketRepository.GetByIdAsync(basketId); @@ -85,27 +34,41 @@ namespace Microsoft.eShopWeb.Services await _basketRepository.UpdateAsync(basket); } - public async Task SetQuantities(int basketId, Dictionary quantities) + public async Task DeleteBasketAsync(int basketId) + { + var basket = await _basketRepository.GetByIdAsync(basketId); + + await _basketRepository.DeleteAsync(basket); + } + + public async Task GetBasketItemCountAsync(string userName) + { + var basketSpec = new BasketWithItemsSpecification(userName); + var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); + if (basket == null) + { + _logger.LogInformation($"No basket found for {userName}"); + return 0; + } + int count = basket.Items.Sum(i => i.Quantity); + _logger.LogInformation($"Basket for {userName} has {count} items."); + return count; + } + + public async Task SetQuantities(int basketId, Dictionary quantities) { var basket = await _basketRepository.GetByIdAsync(basketId); foreach (var item in basket.Items) { if (quantities.TryGetValue(item.Id.ToString(), out var quantity)) { - _logger.LogWarning($"Updating quantity of item ID:{item.Id} to {quantity}."); + _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}."); item.Quantity = quantity; } } await _basketRepository.UpdateAsync(basket); } - public async Task DeleteBasketAsync(int basketId) - { - var basket = await _basketRepository.GetByIdAsync(basketId); - - await _basketRepository.DeleteAsync(basket); - } - public async Task TransferBasketAsync(string anonymousId, string userName) { var basketSpec = new BasketWithItemsSpecification(anonymousId); diff --git a/src/Infrastructure/Services/OrderService.cs b/src/ApplicationCore/Services/OrderService.cs similarity index 97% rename from src/Infrastructure/Services/OrderService.cs rename to src/ApplicationCore/Services/OrderService.cs index d776285..e666182 100644 --- a/src/Infrastructure/Services/OrderService.cs +++ b/src/ApplicationCore/Services/OrderService.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Microsoft.eShopWeb.ApplicationCore.Entities; using System.Collections.Generic; -namespace Infrastructure.Services +namespace ApplicationCore.Services { public class OrderService : IOrderService { diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 4497c83..0edd983 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/src/Web/Controllers/AccountController.cs b/src/Web/Controllers/AccountController.cs index d18b28b..ca01367 100644 --- a/src/Web/Controllers/AccountController.cs +++ b/src/Web/Controllers/AccountController.cs @@ -5,9 +5,8 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Infrastructure.Identity; using System; -using Web; using Microsoft.AspNetCore.Authentication; -using Microsoft.eShopWeb.Interfaces; +using ApplicationCore.Interfaces; namespace Microsoft.eShopWeb.Controllers { diff --git a/src/Web/Controllers/BasketController.cs b/src/Web/Controllers/BasketController.cs index 52034a4..e732324 100644 --- a/src/Web/Controllers/BasketController.cs +++ b/src/Web/Controllers/BasketController.cs @@ -22,8 +22,10 @@ namespace Microsoft.eShopWeb.Controllers private readonly SignInManager _signInManager; private readonly IAppLogger _logger; private readonly IOrderService _orderService; + private readonly IBasketViewModelService _basketViewModelService; public BasketController(IBasketService basketService, + IBasketViewModelService basketViewModelService, IOrderService orderService, IUriComposer uriComposer, SignInManager signInManager, @@ -34,6 +36,7 @@ namespace Microsoft.eShopWeb.Controllers _signInManager = signInManager; _logger = logger; _orderService = orderService; + _basketViewModelService = basketViewModelService; } [HttpGet] @@ -87,10 +90,10 @@ namespace Microsoft.eShopWeb.Controllers { if (_signInManager.IsSignedIn(HttpContext.User)) { - return await _basketService.GetOrCreateBasketForUser(User.Identity.Name); + return await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name); } string anonymousId = GetOrSetBasketCookie(); - return await _basketService.GetOrCreateBasketForUser(anonymousId); + return await _basketViewModelService.GetOrCreateBasketForUser(anonymousId); } private string GetOrSetBasketCookie() diff --git a/src/Web/Interfaces/IBasketService.cs b/src/Web/Interfaces/IBasketService.cs index 360e49c..4bdf2ed 100644 --- a/src/Web/Interfaces/IBasketService.cs +++ b/src/Web/Interfaces/IBasketService.cs @@ -1,15 +1,10 @@ using Microsoft.eShopWeb.ViewModels; -using System.Collections.Generic; using System.Threading.Tasks; namespace Microsoft.eShopWeb.Interfaces { - public interface IBasketService + public interface IBasketViewModelService { Task GetOrCreateBasketForUser(string userName); - Task TransferBasketAsync(string anonymousId, string userName); - Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity); - Task SetQuantities(int basketId, Dictionary quantities); - Task DeleteBasketAsync(int basketId); } } diff --git a/src/Web/Services/BasketViewModelService.cs b/src/Web/Services/BasketViewModelService.cs new file mode 100644 index 0000000..e2a2e2d --- /dev/null +++ b/src/Web/Services/BasketViewModelService.cs @@ -0,0 +1,76 @@ +using ApplicationCore.Interfaces; +using ApplicationCore.Specifications; +using Microsoft.eShopWeb.ApplicationCore.Entities; +using Microsoft.eShopWeb.Interfaces; +using Microsoft.eShopWeb.ViewModels; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopWeb.Services +{ + public class BasketViewModelService : IBasketViewModelService + { + private readonly IAsyncRepository _basketRepository; + private readonly IUriComposer _uriComposer; + private readonly IRepository _itemRepository; + + public BasketViewModelService(IAsyncRepository basketRepository, + IRepository itemRepository, + IUriComposer uriComposer) + { + _basketRepository = basketRepository; + _uriComposer = uriComposer; + _itemRepository = itemRepository; + } + + public async Task GetOrCreateBasketForUser(string userName) + { + var basketSpec = new BasketWithItemsSpecification(userName); + var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); + + if(basket == null) + { + return await CreateBasketForUser(userName); + } + return CreateViewModelFromBasket(basket); + } + + private BasketViewModel CreateViewModelFromBasket(Basket basket) + { + var viewModel = new BasketViewModel(); + viewModel.Id = basket.Id; + viewModel.BuyerId = basket.BuyerId; + viewModel.Items = basket.Items.Select(i => + { + var itemModel = new BasketItemViewModel() + { + Id = i.Id, + UnitPrice = i.UnitPrice, + Quantity = i.Quantity, + CatalogItemId = i.CatalogItemId + + }; + var item = _itemRepository.GetById(i.CatalogItemId); + itemModel.PictureUrl = _uriComposer.ComposePicUri(item.PictureUri); + itemModel.ProductName = item.Name; + return itemModel; + }) + .ToList(); + return viewModel; + } + + private async Task CreateBasketForUser(string userId) + { + var basket = new Basket() { BuyerId = userId }; + await _basketRepository.AddAsync(basket); + + return new BasketViewModel() + { + BuyerId = basket.BuyerId, + Id = basket.Id, + Items = new List() + }; + } + } +} diff --git a/src/Web/Services/CatalogService.cs b/src/Web/Services/CatalogService.cs index 8049b1f..bf68834 100644 --- a/src/Web/Services/CatalogService.cs +++ b/src/Web/Services/CatalogService.cs @@ -11,6 +11,10 @@ using ApplicationCore.Specifications; namespace Microsoft.eShopWeb.Services { + /// + /// This is a UI-specific service so belongs in UI project. It does not contain any business logic and works + /// with UI-specific types (view models and SelectListItem types). + /// public class CatalogService : ICatalogService { private readonly ILogger _logger; diff --git a/src/Web/Startup.cs b/src/Web/Startup.cs index 3bdbee4..b413c6f 100644 --- a/src/Web/Startup.cs +++ b/src/Web/Startup.cs @@ -3,7 +3,6 @@ using ApplicationCore.Services; using Infrastructure.Data; using Infrastructure.Identity; using Infrastructure.Logging; -using Infrastructure.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -93,6 +92,7 @@ namespace Microsoft.eShopWeb services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/Web/ViewComponents/Basket.cs b/src/Web/ViewComponents/Basket.cs index d9fe19c..0c0038e 100644 --- a/src/Web/ViewComponents/Basket.cs +++ b/src/Web/ViewComponents/Basket.cs @@ -12,10 +12,10 @@ namespace Web.ViewComponents { public class Basket : ViewComponent { - private readonly IBasketService _basketService; + private readonly IBasketViewModelService _basketService; private readonly SignInManager _signInManager; - public Basket(IBasketService basketService, + public Basket(IBasketViewModelService basketService, SignInManager signInManager) { _basketService = basketService; diff --git a/src/WebRazorPages/Interfaces/IBasketViewModelService.cs b/src/WebRazorPages/Interfaces/IBasketViewModelService.cs new file mode 100644 index 0000000..975478a --- /dev/null +++ b/src/WebRazorPages/Interfaces/IBasketViewModelService.cs @@ -0,0 +1,11 @@ +using Microsoft.eShopWeb.RazorPages.ViewModels; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.eShopWeb.RazorPages.Interfaces +{ + public interface IBasketViewModelService + { + Task GetOrCreateBasketForUser(string userName); + } +} diff --git a/src/WebRazorPages/Pages/Account/Register.cshtml.cs b/src/WebRazorPages/Pages/Account/Register.cshtml.cs index 5257517..235700d 100644 --- a/src/WebRazorPages/Pages/Account/Register.cshtml.cs +++ b/src/WebRazorPages/Pages/Account/Register.cshtml.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.RazorPages.ViewModels; using Microsoft.AspNetCore.Identity; using Infrastructure.Identity; +using System.ComponentModel.DataAnnotations; namespace Microsoft.eShopWeb.RazorPages.Pages.Account { @@ -23,6 +24,24 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account [BindProperty] public RegisterViewModel UserDetails { get; set; } + public class RegisterViewModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } public async Task OnPost(string returnUrl = "/Index") { @@ -38,7 +57,6 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account AddErrors(result); } return Page(); - } private void AddErrors(IdentityResult result) @@ -48,6 +66,5 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account ModelState.AddModelError("", error.Description); } } - } } diff --git a/src/WebRazorPages/Pages/Account/Signin.cshtml.cs b/src/WebRazorPages/Pages/Account/Signin.cshtml.cs index a7c87d4..cb4557e 100644 --- a/src/WebRazorPages/Pages/Account/Signin.cshtml.cs +++ b/src/WebRazorPages/Pages/Account/Signin.cshtml.cs @@ -1,14 +1,12 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.eShopWeb.RazorPages.ViewModels; -using Microsoft.eShopWeb.RazorPages.Interfaces; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Infrastructure.Identity; using Microsoft.AspNetCore.Authentication; using System; using System.ComponentModel.DataAnnotations; +using ApplicationCore.Interfaces; namespace Microsoft.eShopWeb.RazorPages.Pages.Account { diff --git a/src/WebRazorPages/Pages/Basket/Index.cshtml.cs b/src/WebRazorPages/Pages/Basket/Index.cshtml.cs index 3648b1e..da53384 100644 --- a/src/WebRazorPages/Pages/Basket/Index.cshtml.cs +++ b/src/WebRazorPages/Pages/Basket/Index.cshtml.cs @@ -22,8 +22,10 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket private readonly IAppLogger _logger; private readonly IOrderService _orderService; private string _username = null; + private readonly IBasketViewModelService _basketViewModelService; public IndexModel(IBasketService basketService, + IBasketViewModelService basketViewModelService, IUriComposer uriComposer, SignInManager signInManager, IAppLogger logger, @@ -34,6 +36,7 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket _signInManager = signInManager; _logger = logger; _orderService = orderService; + _basketViewModelService = basketViewModelService; } public BasketViewModel BasketModel { get; set; } = new BasketViewModel(); @@ -83,12 +86,12 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket { if (_signInManager.IsSignedIn(HttpContext.User)) { - BasketModel = await _basketService.GetOrCreateBasketForUser(User.Identity.Name); + BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name); } else { GetOrSetBasketCookieAndUserName(); - BasketModel = await _basketService.GetOrCreateBasketForUser(_username); + BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username); } } diff --git a/src/WebRazorPages/Pages/Components/Basket/Default.cshtml b/src/WebRazorPages/Pages/Components/Basket/Default.cshtml index 25b4b15..acf97f7 100644 --- a/src/WebRazorPages/Pages/Components/Basket/Default.cshtml +++ b/src/WebRazorPages/Pages/Components/Basket/Default.cshtml @@ -1,4 +1,5 @@ -@model BasketComponentViewModel +@using Microsoft.eShopWeb.RazorPages.ViewComponents +@model Basket.BasketComponentViewModel @{ ViewData["Title"] = "My Basket"; diff --git a/src/WebRazorPages/Pages/Order/Detail.cshtml.cs b/src/WebRazorPages/Pages/Order/Detail.cshtml.cs index c5e1eaf..96c45fc 100644 --- a/src/WebRazorPages/Pages/Order/Detail.cshtml.cs +++ b/src/WebRazorPages/Pages/Order/Detail.cshtml.cs @@ -1,8 +1,10 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.eShopWeb.RazorPages.ViewModels; using ApplicationCore.Interfaces; using System.Linq; +using System; +using ApplicationCore.Entities.OrderAggregate; +using System.Collections.Generic; namespace Microsoft.eShopWeb.RazorPages.Pages.Order { @@ -17,6 +19,27 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Order public OrderViewModel OrderDetails { get; set; } = new OrderViewModel(); + public class OrderViewModel + { + public int OrderNumber { get; set; } + public DateTimeOffset OrderDate { get; set; } + public decimal Total { get; set; } + public string Status { get; set; } + + public Address ShippingAddress { get; set; } + + public List OrderItems { get; set; } = new List(); + } + + public class OrderItemViewModel + { + public int ProductId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal Discount { get; set; } + public int Units { get; set; } + public string PictureUrl { get; set; } + } public async Task OnGet(int orderId) { diff --git a/src/WebRazorPages/Pages/Order/Index.cshtml.cs b/src/WebRazorPages/Pages/Order/Index.cshtml.cs index 70245e7..72444db 100644 --- a/src/WebRazorPages/Pages/Order/Index.cshtml.cs +++ b/src/WebRazorPages/Pages/Order/Index.cshtml.cs @@ -1,10 +1,10 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.eShopWeb.RazorPages.ViewModels; using ApplicationCore.Interfaces; using ApplicationCore.Specifications; using System.Collections.Generic; using System.Linq; +using System; namespace Microsoft.eShopWeb.RazorPages.Pages.Order { @@ -17,28 +17,25 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Order _orderRepository = orderRepository; } - public List Orders { get; set; } = new List(); + public List Orders { get; set; } = new List(); + public class OrderSummary + { + public int OrderNumber { get; set; } + public DateTimeOffset OrderDate { get; set; } + public decimal Total { get; set; } + public string Status { get; set; } + } public async Task OnGet() { var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name)); Orders = orders - .Select(o => new OrderViewModel() + .Select(o => new OrderSummary() { OrderDate = o.OrderDate, - OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel() - { - Discount = 0, - PictureUrl = oi.ItemOrdered.PictureUri, - ProductId = oi.ItemOrdered.CatalogItemId, - ProductName = oi.ItemOrdered.ProductName, - UnitPrice = oi.UnitPrice, - Units = oi.Units - }).ToList(), OrderNumber = o.Id, - ShippingAddress = o.ShipToAddress, Status = "Pending", Total = o.Total() diff --git a/src/WebRazorPages/Pages/_LoginPartial.cshtml b/src/WebRazorPages/Pages/_LoginPartial.cshtml index 373148e..58740e6 100644 --- a/src/WebRazorPages/Pages/_LoginPartial.cshtml +++ b/src/WebRazorPages/Pages/_LoginPartial.cshtml @@ -30,7 +30,7 @@
- @await Component.InvokeAsync("Basket", User.Identity.Name) + @await Component.InvokeAsync("Basket")
} diff --git a/src/WebRazorPages/Services/BasketService.cs b/src/WebRazorPages/Services/BasketViewModelService.cs similarity index 55% rename from src/WebRazorPages/Services/BasketService.cs rename to src/WebRazorPages/Services/BasketViewModelService.cs index 770e654..7a38244 100644 --- a/src/WebRazorPages/Services/BasketService.cs +++ b/src/WebRazorPages/Services/BasketViewModelService.cs @@ -9,21 +9,18 @@ using Microsoft.eShopWeb.RazorPages.ViewModels; namespace Microsoft.eShopWeb.RazorPages.Services { - public class BasketService : IBasketService + public class BasketViewModelService : IBasketViewModelService { private readonly IAsyncRepository _basketRepository; private readonly IUriComposer _uriComposer; - private readonly IAppLogger _logger; private readonly IRepository _itemRepository; - public BasketService(IAsyncRepository basketRepository, + public BasketViewModelService(IAsyncRepository basketRepository, IRepository itemRepository, - IUriComposer uriComposer, - IAppLogger logger) + IUriComposer uriComposer) { _basketRepository = basketRepository; _uriComposer = uriComposer; - this._logger = logger; _itemRepository = itemRepository; } @@ -32,7 +29,7 @@ namespace Microsoft.eShopWeb.RazorPages.Services var basketSpec = new BasketWithItemsSpecification(userName); var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); - if(basket == null) + if (basket == null) { return await CreateBasketForUser(userName); } @@ -63,7 +60,7 @@ namespace Microsoft.eShopWeb.RazorPages.Services return viewModel; } - public async Task CreateBasketForUser(string userId) + private async Task CreateBasketForUser(string userId) { var basket = new Basket() { BuyerId = userId }; await _basketRepository.AddAsync(basket); @@ -75,44 +72,5 @@ namespace Microsoft.eShopWeb.RazorPages.Services Items = new List() }; } - - public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity) - { - var basket = await _basketRepository.GetByIdAsync(basketId); - - basket.AddItem(catalogItemId, price, quantity); - - await _basketRepository.UpdateAsync(basket); - } - - public async Task SetQuantities(int basketId, Dictionary quantities) - { - var basket = await _basketRepository.GetByIdAsync(basketId); - foreach (var item in basket.Items) - { - if (quantities.TryGetValue(item.Id.ToString(), out var quantity)) - { - _logger.LogWarning($"Updating quantity of item ID:{item.Id} to {quantity}."); - item.Quantity = quantity; - } - } - await _basketRepository.UpdateAsync(basket); - } - - public async Task DeleteBasketAsync(int basketId) - { - var basket = await _basketRepository.GetByIdAsync(basketId); - - await _basketRepository.DeleteAsync(basket); - } - - public async Task TransferBasketAsync(string anonymousId, string userName) - { - var basketSpec = new BasketWithItemsSpecification(anonymousId); - var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault(); - if (basket == null) return; - basket.BuyerId = userName; - await _basketRepository.UpdateAsync(basket); - } } } diff --git a/src/WebRazorPages/Services/CatalogService.cs b/src/WebRazorPages/Services/CatalogService.cs index 52fec25..b97ee54 100644 --- a/src/WebRazorPages/Services/CatalogService.cs +++ b/src/WebRazorPages/Services/CatalogService.cs @@ -12,6 +12,10 @@ using System.Threading.Tasks; namespace Microsoft.eShopWeb.RazorPages.Services { + /// + /// This is a UI-specific service so belongs in UI project. It does not contain any business logic and works + /// with UI-specific types (view models and SelectListItem types). + /// public class CatalogService : ICatalogService { private readonly ILogger _logger; diff --git a/src/WebRazorPages/Startup.cs b/src/WebRazorPages/Startup.cs index ae919e2..60ba8be 100644 --- a/src/WebRazorPages/Startup.cs +++ b/src/WebRazorPages/Startup.cs @@ -3,7 +3,6 @@ using ApplicationCore.Services; using Infrastructure.Data; using Infrastructure.Identity; using Infrastructure.Logging; -using Infrastructure.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -93,6 +92,7 @@ namespace Microsoft.eShopWeb.RazorPages services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/WebRazorPages/ViewComponents/Basket.cs b/src/WebRazorPages/ViewComponents/Basket.cs index 4596555..1b1b4d3 100644 --- a/src/WebRazorPages/ViewComponents/Basket.cs +++ b/src/WebRazorPages/ViewComponents/Basket.cs @@ -1,10 +1,8 @@ -using Infrastructure.Identity; +using ApplicationCore.Interfaces; +using Infrastructure.Identity; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopWeb.RazorPages.Interfaces; -using Microsoft.eShopWeb.RazorPages.ViewModels; -using System.Linq; using System.Threading.Tasks; namespace Microsoft.eShopWeb.RazorPages.ViewComponents @@ -21,22 +19,26 @@ namespace Microsoft.eShopWeb.RazorPages.ViewComponents _signInManager = signInManager; } - public async Task InvokeAsync(string userName) + public async Task InvokeAsync() { var vm = new BasketComponentViewModel(); - vm.ItemsCount = (await GetBasketViewModelAsync()).Items.Sum(i => i.Quantity); + string userName = GetUsername(); + vm.ItemsCount = (await _basketService.GetBasketItemCountAsync(userName)); return View(vm); } - private async Task GetBasketViewModelAsync() + public class BasketComponentViewModel + { + public int ItemsCount { get; set; } + } + + private string GetUsername() { if (_signInManager.IsSignedIn(HttpContext.User)) { - return await _basketService.GetOrCreateBasketForUser(User.Identity.Name); + return User.Identity.Name; } - string anonymousId = GetBasketIdFromCookie(); - if (anonymousId == null) return new BasketViewModel(); - return await _basketService.GetOrCreateBasketForUser(anonymousId); + return GetBasketIdFromCookie(); } private string GetBasketIdFromCookie() diff --git a/src/WebRazorPages/ViewModels/BasketComponentViewModel.cs b/src/WebRazorPages/ViewModels/BasketComponentViewModel.cs deleted file mode 100644 index 44e3e29..0000000 --- a/src/WebRazorPages/ViewModels/BasketComponentViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Microsoft.eShopWeb.RazorPages.ViewModels -{ - public class BasketComponentViewModel - { - public int ItemsCount { get; set; } - } -} diff --git a/src/WebRazorPages/ViewModels/OrderItemViewModel.cs b/src/WebRazorPages/ViewModels/OrderItemViewModel.cs deleted file mode 100644 index 4fd7282..0000000 --- a/src/WebRazorPages/ViewModels/OrderItemViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Microsoft.eShopWeb.RazorPages.ViewModels -{ - public class OrderItemViewModel - { - public int ProductId { get; set; } - - public string ProductName { get; set; } - - public decimal UnitPrice { get; set; } - - public decimal Discount { get; set; } - - public int Units { get; set; } - - public string PictureUrl { get; set; } - } - -} diff --git a/src/WebRazorPages/ViewModels/OrderViewModel.cs b/src/WebRazorPages/ViewModels/OrderViewModel.cs deleted file mode 100644 index b1e2935..0000000 --- a/src/WebRazorPages/ViewModels/OrderViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ApplicationCore.Entities.OrderAggregate; -using System; -using System.Collections.Generic; - -namespace Microsoft.eShopWeb.RazorPages.ViewModels -{ - public class OrderViewModel - { - public int OrderNumber { get; set; } - public DateTimeOffset OrderDate { get; set; } - public decimal Total { get; set; } - public string Status { get; set; } - - public Address ShippingAddress { get; set; } - - public List OrderItems { get; set; } = new List(); - } -} diff --git a/src/WebRazorPages/ViewModels/RegisterViewModel.cs b/src/WebRazorPages/ViewModels/RegisterViewModel.cs deleted file mode 100644 index c0cc0bd..0000000 --- a/src/WebRazorPages/ViewModels/RegisterViewModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace Microsoft.eShopWeb.RazorPages.ViewModels -{ - public class RegisterViewModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } -}