Refactoring Services (#61)
* Refactoring ViewModels into Razor Pages models * Cleaning up Basket viewcomponent * Refactoring services. Fixed bug in basket item counter.
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Interfaces
|
namespace ApplicationCore.Interfaces
|
||||||
{
|
{
|
||||||
public interface IBasketService
|
public interface IBasketService
|
||||||
{
|
{
|
||||||
Task<BasketViewModel> GetOrCreateBasketForUser(string userName);
|
Task<int> GetBasketItemCountAsync(string userName);
|
||||||
Task TransferBasketAsync(string anonymousId, string userName);
|
Task TransferBasketAsync(string anonymousId, string userName);
|
||||||
Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity);
|
Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity);
|
||||||
Task SetQuantities(int basketId, Dictionary<string, int> quantities);
|
Task SetQuantities(int basketId, Dictionary<string, int> quantities);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using ApplicationCore.Entities.OrderAggregate;
|
using ApplicationCore.Entities.OrderAggregate;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ApplicationCore.Interfaces
|
namespace ApplicationCore.Interfaces
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
using ApplicationCore.Interfaces;
|
using ApplicationCore.Interfaces;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
using ApplicationCore.Specifications;
|
using ApplicationCore.Specifications;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
using Microsoft.eShopWeb.Interfaces;
|
|
||||||
using Microsoft.eShopWeb.ViewModels;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Services
|
namespace ApplicationCore.Services
|
||||||
{
|
{
|
||||||
public class BasketService : IBasketService
|
public class BasketService : IBasketService
|
||||||
{
|
{
|
||||||
@@ -27,55 +25,6 @@ namespace Microsoft.eShopWeb.Services
|
|||||||
_itemRepository = itemRepository;
|
_itemRepository = itemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BasketViewModel> 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<BasketViewModel> 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<BasketItemViewModel>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity)
|
public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity)
|
||||||
{
|
{
|
||||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||||
@@ -85,6 +34,27 @@ namespace Microsoft.eShopWeb.Services
|
|||||||
await _basketRepository.UpdateAsync(basket);
|
await _basketRepository.UpdateAsync(basket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteBasketAsync(int basketId)
|
||||||
|
{
|
||||||
|
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||||
|
|
||||||
|
await _basketRepository.DeleteAsync(basket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> 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<string, int> quantities)
|
public async Task SetQuantities(int basketId, Dictionary<string, int> quantities)
|
||||||
{
|
{
|
||||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||||
@@ -92,20 +62,13 @@ namespace Microsoft.eShopWeb.Services
|
|||||||
{
|
{
|
||||||
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
|
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;
|
item.Quantity = quantity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _basketRepository.UpdateAsync(basket);
|
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)
|
public async Task TransferBasketAsync(string anonymousId, string userName)
|
||||||
{
|
{
|
||||||
var basketSpec = new BasketWithItemsSpecification(anonymousId);
|
var basketSpec = new BasketWithItemsSpecification(anonymousId);
|
||||||
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Infrastructure.Services
|
namespace ApplicationCore.Services
|
||||||
{
|
{
|
||||||
public class OrderService : IOrderService
|
public class OrderService : IOrderService
|
||||||
{
|
{
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Data\Migrations\" />
|
<Folder Include="Data\Migrations\" />
|
||||||
|
<Folder Include="Services\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,9 +5,8 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Infrastructure.Identity;
|
using Infrastructure.Identity;
|
||||||
using System;
|
using System;
|
||||||
using Web;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.eShopWeb.Interfaces;
|
using ApplicationCore.Interfaces;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Controllers
|
namespace Microsoft.eShopWeb.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
private readonly IAppLogger<BasketController> _logger;
|
private readonly IAppLogger<BasketController> _logger;
|
||||||
private readonly IOrderService _orderService;
|
private readonly IOrderService _orderService;
|
||||||
|
private readonly IBasketViewModelService _basketViewModelService;
|
||||||
|
|
||||||
public BasketController(IBasketService basketService,
|
public BasketController(IBasketService basketService,
|
||||||
|
IBasketViewModelService basketViewModelService,
|
||||||
IOrderService orderService,
|
IOrderService orderService,
|
||||||
IUriComposer uriComposer,
|
IUriComposer uriComposer,
|
||||||
SignInManager<ApplicationUser> signInManager,
|
SignInManager<ApplicationUser> signInManager,
|
||||||
@@ -34,6 +36,7 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_orderService = orderService;
|
_orderService = orderService;
|
||||||
|
_basketViewModelService = basketViewModelService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -87,10 +90,10 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
{
|
{
|
||||||
if (_signInManager.IsSignedIn(HttpContext.User))
|
if (_signInManager.IsSignedIn(HttpContext.User))
|
||||||
{
|
{
|
||||||
return await _basketService.GetOrCreateBasketForUser(User.Identity.Name);
|
return await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
|
||||||
}
|
}
|
||||||
string anonymousId = GetOrSetBasketCookie();
|
string anonymousId = GetOrSetBasketCookie();
|
||||||
return await _basketService.GetOrCreateBasketForUser(anonymousId);
|
return await _basketViewModelService.GetOrCreateBasketForUser(anonymousId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetOrSetBasketCookie()
|
private string GetOrSetBasketCookie()
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
using Microsoft.eShopWeb.ViewModels;
|
using Microsoft.eShopWeb.ViewModels;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Interfaces
|
namespace Microsoft.eShopWeb.Interfaces
|
||||||
{
|
{
|
||||||
public interface IBasketService
|
public interface IBasketViewModelService
|
||||||
{
|
{
|
||||||
Task<BasketViewModel> GetOrCreateBasketForUser(string userName);
|
Task<BasketViewModel> GetOrCreateBasketForUser(string userName);
|
||||||
Task TransferBasketAsync(string anonymousId, string userName);
|
|
||||||
Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity);
|
|
||||||
Task SetQuantities(int basketId, Dictionary<string, int> quantities);
|
|
||||||
Task DeleteBasketAsync(int basketId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/Web/Services/BasketViewModelService.cs
Normal file
76
src/Web/Services/BasketViewModelService.cs
Normal file
@@ -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<Basket> _basketRepository;
|
||||||
|
private readonly IUriComposer _uriComposer;
|
||||||
|
private readonly IRepository<CatalogItem> _itemRepository;
|
||||||
|
|
||||||
|
public BasketViewModelService(IAsyncRepository<Basket> basketRepository,
|
||||||
|
IRepository<CatalogItem> itemRepository,
|
||||||
|
IUriComposer uriComposer)
|
||||||
|
{
|
||||||
|
_basketRepository = basketRepository;
|
||||||
|
_uriComposer = uriComposer;
|
||||||
|
_itemRepository = itemRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BasketViewModel> 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<BasketViewModel> 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<BasketItemViewModel>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@ using ApplicationCore.Specifications;
|
|||||||
|
|
||||||
namespace Microsoft.eShopWeb.Services
|
namespace Microsoft.eShopWeb.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
public class CatalogService : ICatalogService
|
public class CatalogService : ICatalogService
|
||||||
{
|
{
|
||||||
private readonly ILogger<CatalogService> _logger;
|
private readonly ILogger<CatalogService> _logger;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using ApplicationCore.Services;
|
|||||||
using Infrastructure.Data;
|
using Infrastructure.Data;
|
||||||
using Infrastructure.Identity;
|
using Infrastructure.Identity;
|
||||||
using Infrastructure.Logging;
|
using Infrastructure.Logging;
|
||||||
using Infrastructure.Services;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -93,6 +92,7 @@ namespace Microsoft.eShopWeb
|
|||||||
|
|
||||||
services.AddScoped<ICatalogService, CachedCatalogService>();
|
services.AddScoped<ICatalogService, CachedCatalogService>();
|
||||||
services.AddScoped<IBasketService, BasketService>();
|
services.AddScoped<IBasketService, BasketService>();
|
||||||
|
services.AddScoped<IBasketViewModelService, BasketViewModelService>();
|
||||||
services.AddScoped<IOrderService, OrderService>();
|
services.AddScoped<IOrderService, OrderService>();
|
||||||
services.AddScoped<IOrderRepository, OrderRepository>();
|
services.AddScoped<IOrderRepository, OrderRepository>();
|
||||||
services.AddScoped<CatalogService>();
|
services.AddScoped<CatalogService>();
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ namespace Web.ViewComponents
|
|||||||
{
|
{
|
||||||
public class Basket : ViewComponent
|
public class Basket : ViewComponent
|
||||||
{
|
{
|
||||||
private readonly IBasketService _basketService;
|
private readonly IBasketViewModelService _basketService;
|
||||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
public Basket(IBasketService basketService,
|
public Basket(IBasketViewModelService basketService,
|
||||||
SignInManager<ApplicationUser> signInManager)
|
SignInManager<ApplicationUser> signInManager)
|
||||||
{
|
{
|
||||||
_basketService = basketService;
|
_basketService = basketService;
|
||||||
|
|||||||
11
src/WebRazorPages/Interfaces/IBasketViewModelService.cs
Normal file
11
src/WebRazorPages/Interfaces/IBasketViewModelService.cs
Normal file
@@ -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<BasketViewModel> GetOrCreateBasketForUser(string userName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Infrastructure.Identity;
|
using Infrastructure.Identity;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
||||||
{
|
{
|
||||||
@@ -23,6 +24,24 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
|||||||
[BindProperty]
|
[BindProperty]
|
||||||
public RegisterViewModel UserDetails { get; set; }
|
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<IActionResult> OnPost(string returnUrl = "/Index")
|
public async Task<IActionResult> OnPost(string returnUrl = "/Index")
|
||||||
{
|
{
|
||||||
@@ -38,7 +57,6 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
|||||||
AddErrors(result);
|
AddErrors(result);
|
||||||
}
|
}
|
||||||
return Page();
|
return Page();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddErrors(IdentityResult result)
|
private void AddErrors(IdentityResult result)
|
||||||
@@ -48,6 +66,5 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
|||||||
ModelState.AddModelError("", error.Description);
|
ModelState.AddModelError("", error.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
|
||||||
using Microsoft.eShopWeb.RazorPages.Interfaces;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Infrastructure.Identity;
|
using Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using ApplicationCore.Interfaces;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
namespace Microsoft.eShopWeb.RazorPages.Pages.Account
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket
|
|||||||
private readonly IAppLogger<IndexModel> _logger;
|
private readonly IAppLogger<IndexModel> _logger;
|
||||||
private readonly IOrderService _orderService;
|
private readonly IOrderService _orderService;
|
||||||
private string _username = null;
|
private string _username = null;
|
||||||
|
private readonly IBasketViewModelService _basketViewModelService;
|
||||||
|
|
||||||
public IndexModel(IBasketService basketService,
|
public IndexModel(IBasketService basketService,
|
||||||
|
IBasketViewModelService basketViewModelService,
|
||||||
IUriComposer uriComposer,
|
IUriComposer uriComposer,
|
||||||
SignInManager<ApplicationUser> signInManager,
|
SignInManager<ApplicationUser> signInManager,
|
||||||
IAppLogger<IndexModel> logger,
|
IAppLogger<IndexModel> logger,
|
||||||
@@ -34,6 +36,7 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket
|
|||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_orderService = orderService;
|
_orderService = orderService;
|
||||||
|
_basketViewModelService = basketViewModelService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasketViewModel BasketModel { get; set; } = new BasketViewModel();
|
public BasketViewModel BasketModel { get; set; } = new BasketViewModel();
|
||||||
@@ -83,12 +86,12 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Basket
|
|||||||
{
|
{
|
||||||
if (_signInManager.IsSignedIn(HttpContext.User))
|
if (_signInManager.IsSignedIn(HttpContext.User))
|
||||||
{
|
{
|
||||||
BasketModel = await _basketService.GetOrCreateBasketForUser(User.Identity.Name);
|
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GetOrSetBasketCookieAndUserName();
|
GetOrSetBasketCookieAndUserName();
|
||||||
BasketModel = await _basketService.GetOrCreateBasketForUser(_username);
|
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@model BasketComponentViewModel
|
@using Microsoft.eShopWeb.RazorPages.ViewComponents
|
||||||
|
@model Basket.BasketComponentViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "My Basket";
|
ViewData["Title"] = "My Basket";
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
|
||||||
using ApplicationCore.Interfaces;
|
using ApplicationCore.Interfaces;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using ApplicationCore.Entities.OrderAggregate;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
||||||
{
|
{
|
||||||
@@ -17,6 +19,27 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
|||||||
|
|
||||||
public OrderViewModel OrderDetails { get; set; } = new OrderViewModel();
|
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<OrderItemViewModel> OrderItems { get; set; } = new List<OrderItemViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
public async Task OnGet(int orderId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
|
||||||
using ApplicationCore.Interfaces;
|
using ApplicationCore.Interfaces;
|
||||||
using ApplicationCore.Specifications;
|
using ApplicationCore.Specifications;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
||||||
{
|
{
|
||||||
@@ -17,28 +17,25 @@ namespace Microsoft.eShopWeb.RazorPages.Pages.Order
|
|||||||
_orderRepository = orderRepository;
|
_orderRepository = orderRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<OrderViewModel> Orders { get; set; } = new List<OrderViewModel>();
|
public List<OrderSummary> Orders { get; set; } = new List<OrderSummary>();
|
||||||
|
|
||||||
|
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()
|
public async Task OnGet()
|
||||||
{
|
{
|
||||||
var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name));
|
var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name));
|
||||||
|
|
||||||
Orders = orders
|
Orders = orders
|
||||||
.Select(o => new OrderViewModel()
|
.Select(o => new OrderSummary()
|
||||||
{
|
{
|
||||||
OrderDate = o.OrderDate,
|
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,
|
OrderNumber = o.Id,
|
||||||
ShippingAddress = o.ShipToAddress,
|
|
||||||
Status = "Pending",
|
Status = "Pending",
|
||||||
Total = o.Total()
|
Total = o.Total()
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="col-lg-1 col-xs-12">
|
<section class="col-lg-1 col-xs-12">
|
||||||
@await Component.InvokeAsync("Basket", User.Identity.Name)
|
@await Component.InvokeAsync("Basket")
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,18 @@ using Microsoft.eShopWeb.RazorPages.ViewModels;
|
|||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Services
|
namespace Microsoft.eShopWeb.RazorPages.Services
|
||||||
{
|
{
|
||||||
public class BasketService : IBasketService
|
public class BasketViewModelService : IBasketViewModelService
|
||||||
{
|
{
|
||||||
private readonly IAsyncRepository<Basket> _basketRepository;
|
private readonly IAsyncRepository<Basket> _basketRepository;
|
||||||
private readonly IUriComposer _uriComposer;
|
private readonly IUriComposer _uriComposer;
|
||||||
private readonly IAppLogger<BasketService> _logger;
|
|
||||||
private readonly IRepository<CatalogItem> _itemRepository;
|
private readonly IRepository<CatalogItem> _itemRepository;
|
||||||
|
|
||||||
public BasketService(IAsyncRepository<Basket> basketRepository,
|
public BasketViewModelService(IAsyncRepository<Basket> basketRepository,
|
||||||
IRepository<CatalogItem> itemRepository,
|
IRepository<CatalogItem> itemRepository,
|
||||||
IUriComposer uriComposer,
|
IUriComposer uriComposer)
|
||||||
IAppLogger<BasketService> logger)
|
|
||||||
{
|
{
|
||||||
_basketRepository = basketRepository;
|
_basketRepository = basketRepository;
|
||||||
_uriComposer = uriComposer;
|
_uriComposer = uriComposer;
|
||||||
this._logger = logger;
|
|
||||||
_itemRepository = itemRepository;
|
_itemRepository = itemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +60,7 @@ namespace Microsoft.eShopWeb.RazorPages.Services
|
|||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BasketViewModel> CreateBasketForUser(string userId)
|
private async Task<BasketViewModel> CreateBasketForUser(string userId)
|
||||||
{
|
{
|
||||||
var basket = new Basket() { BuyerId = userId };
|
var basket = new Basket() { BuyerId = userId };
|
||||||
await _basketRepository.AddAsync(basket);
|
await _basketRepository.AddAsync(basket);
|
||||||
@@ -75,44 +72,5 @@ namespace Microsoft.eShopWeb.RazorPages.Services
|
|||||||
Items = new List<BasketItemViewModel>()
|
Items = new List<BasketItemViewModel>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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<string,int> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.Services
|
namespace Microsoft.eShopWeb.RazorPages.Services
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
public class CatalogService : ICatalogService
|
public class CatalogService : ICatalogService
|
||||||
{
|
{
|
||||||
private readonly ILogger<CatalogService> _logger;
|
private readonly ILogger<CatalogService> _logger;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using ApplicationCore.Services;
|
|||||||
using Infrastructure.Data;
|
using Infrastructure.Data;
|
||||||
using Infrastructure.Identity;
|
using Infrastructure.Identity;
|
||||||
using Infrastructure.Logging;
|
using Infrastructure.Logging;
|
||||||
using Infrastructure.Services;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -93,6 +92,7 @@ namespace Microsoft.eShopWeb.RazorPages
|
|||||||
|
|
||||||
services.AddScoped<ICatalogService, CachedCatalogService>();
|
services.AddScoped<ICatalogService, CachedCatalogService>();
|
||||||
services.AddScoped<IBasketService, BasketService>();
|
services.AddScoped<IBasketService, BasketService>();
|
||||||
|
services.AddScoped<IBasketViewModelService, BasketViewModelService>();
|
||||||
services.AddScoped<IOrderService, OrderService>();
|
services.AddScoped<IOrderService, OrderService>();
|
||||||
services.AddScoped<IOrderRepository, OrderRepository>();
|
services.AddScoped<IOrderRepository, OrderRepository>();
|
||||||
services.AddScoped<CatalogService>();
|
services.AddScoped<CatalogService>();
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using Infrastructure.Identity;
|
using ApplicationCore.Interfaces;
|
||||||
|
using Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.eShopWeb.RazorPages.Interfaces;
|
|
||||||
using Microsoft.eShopWeb.RazorPages.ViewModels;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.RazorPages.ViewComponents
|
namespace Microsoft.eShopWeb.RazorPages.ViewComponents
|
||||||
@@ -21,22 +19,26 @@ namespace Microsoft.eShopWeb.RazorPages.ViewComponents
|
|||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(string userName)
|
public async Task<IViewComponentResult> InvokeAsync()
|
||||||
{
|
{
|
||||||
var vm = new BasketComponentViewModel();
|
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);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<BasketViewModel> GetBasketViewModelAsync()
|
public class BasketComponentViewModel
|
||||||
|
{
|
||||||
|
public int ItemsCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUsername()
|
||||||
{
|
{
|
||||||
if (_signInManager.IsSignedIn(HttpContext.User))
|
if (_signInManager.IsSignedIn(HttpContext.User))
|
||||||
{
|
{
|
||||||
return await _basketService.GetOrCreateBasketForUser(User.Identity.Name);
|
return User.Identity.Name;
|
||||||
}
|
}
|
||||||
string anonymousId = GetBasketIdFromCookie();
|
return GetBasketIdFromCookie();
|
||||||
if (anonymousId == null) return new BasketViewModel();
|
|
||||||
return await _basketService.GetOrCreateBasketForUser(anonymousId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetBasketIdFromCookie()
|
private string GetBasketIdFromCookie()
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Microsoft.eShopWeb.RazorPages.ViewModels
|
|
||||||
{
|
|
||||||
public class BasketComponentViewModel
|
|
||||||
{
|
|
||||||
public int ItemsCount { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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<OrderItemViewModel> OrderItems { get; set; } = new List<OrderItemViewModel>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user