Shady nagy/net6 (#614)
* udated to .net6 * used the .net6 version RC2 * added editconfig. * App core new Scoped Namespaces style. * BlazorAdmin new Scoped Namespaces style. * Blazor Shared new Scoped Namespaces style. * Infra new Scoped Namespaces style. * public api new Scoped Namespaces style. * web new Scoped Namespaces style. * FunctionalTests new Scoped Namespaces style. * Integrational tests new Scoped Namespaces style. * unit tests new Scoped Namespaces style. * update github action. * update github action. * change the global.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace Microsoft.eShopWeb
|
||||
namespace Microsoft.eShopWeb;
|
||||
|
||||
public class CatalogSettings
|
||||
{
|
||||
public class CatalogSettings
|
||||
{
|
||||
public string CatalogBaseUrl { get; set; }
|
||||
}
|
||||
public string CatalogBaseUrl { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Constants
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Constants;
|
||||
|
||||
public class AuthorizationConstants
|
||||
{
|
||||
public class AuthorizationConstants
|
||||
{
|
||||
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
|
||||
// TODO: Don't use this in production
|
||||
public const string DEFAULT_PASSWORD = "Pass@word1";
|
||||
// TODO: Don't use this in production
|
||||
public const string DEFAULT_PASSWORD = "Pass@word1";
|
||||
|
||||
// TODO: Change this to an environment variable
|
||||
public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
}
|
||||
// TODO: Change this to an environment variable
|
||||
public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
|
||||
// Using non-generic integer types for simplicity and to ease caching logic
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
|
||||
// Using non-generic integer types for simplicity and to ease caching logic
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
public virtual int Id { get; protected set; }
|
||||
}
|
||||
public virtual int Id { get; protected set; }
|
||||
}
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
public class Basket : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Basket : BaseEntity, IAggregateRoot
|
||||
public string BuyerId { get; private set; }
|
||||
private readonly List<BasketItem> _items = new List<BasketItem>();
|
||||
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();
|
||||
|
||||
public Basket(string buyerId)
|
||||
{
|
||||
public string BuyerId { get; private set; }
|
||||
private readonly List<BasketItem> _items = new List<BasketItem>();
|
||||
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
|
||||
public Basket(string buyerId)
|
||||
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
|
||||
{
|
||||
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
|
||||
return;
|
||||
}
|
||||
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
|
||||
existingItem.AddQuantity(quantity);
|
||||
}
|
||||
|
||||
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
|
||||
{
|
||||
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
|
||||
{
|
||||
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
|
||||
return;
|
||||
}
|
||||
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
|
||||
existingItem.AddQuantity(quantity);
|
||||
}
|
||||
public void RemoveEmptyItems()
|
||||
{
|
||||
_items.RemoveAll(i => i.Quantity == 0);
|
||||
}
|
||||
|
||||
public void RemoveEmptyItems()
|
||||
{
|
||||
_items.RemoveAll(i => i.Quantity == 0);
|
||||
}
|
||||
|
||||
public void SetNewBuyerId(string buyerId)
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
public void SetNewBuyerId(string buyerId)
|
||||
{
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
using Ardalis.GuardClauses;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
public class BasketItem : BaseEntity
|
||||
{
|
||||
public class BasketItem : BaseEntity
|
||||
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Quantity { get; private set; }
|
||||
public int CatalogItemId { get; private set; }
|
||||
public int BasketId { get; private set; }
|
||||
|
||||
public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
|
||||
{
|
||||
CatalogItemId = catalogItemId;
|
||||
UnitPrice = unitPrice;
|
||||
SetQuantity(quantity);
|
||||
}
|
||||
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Quantity { get; private set; }
|
||||
public int CatalogItemId { get; private set; }
|
||||
public int BasketId { get; private set; }
|
||||
public void AddQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
|
||||
{
|
||||
CatalogItemId = catalogItemId;
|
||||
UnitPrice = unitPrice;
|
||||
SetQuantity(quantity);
|
||||
}
|
||||
Quantity += quantity;
|
||||
}
|
||||
|
||||
public void AddQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
public void SetQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
Quantity += quantity;
|
||||
}
|
||||
|
||||
public void SetQuantity(int quantity)
|
||||
{
|
||||
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
|
||||
|
||||
Quantity = quantity;
|
||||
}
|
||||
Quantity = quantity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Collections.Generic;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
|
||||
|
||||
public class Buyer : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Buyer : BaseEntity, IAggregateRoot
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
private Buyer()
|
||||
{
|
||||
public string IdentityGuid { get; private set; }
|
||||
// required by EF
|
||||
}
|
||||
|
||||
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
private Buyer()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Buyer(string identity) : this()
|
||||
{
|
||||
Guard.Against.NullOrEmpty(identity, nameof(identity));
|
||||
IdentityGuid = identity;
|
||||
}
|
||||
public Buyer(string identity) : this()
|
||||
{
|
||||
Guard.Against.NullOrEmpty(identity, nameof(identity));
|
||||
IdentityGuid = identity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
|
||||
|
||||
public class PaymentMethod : BaseEntity
|
||||
{
|
||||
public class PaymentMethod : BaseEntity
|
||||
{
|
||||
public string Alias { get; private set; }
|
||||
public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
public string Last4 { get; private set; }
|
||||
}
|
||||
public string Alias { get; private set; }
|
||||
public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
public string Last4 { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogBrand : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogBrand : BaseEntity, IAggregateRoot
|
||||
public string Brand { get; private set; }
|
||||
public CatalogBrand(string brand)
|
||||
{
|
||||
public string Brand { get; private set; }
|
||||
public CatalogBrand(string brand)
|
||||
{
|
||||
Brand = brand;
|
||||
}
|
||||
Brand = brand;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,65 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogItem : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogItem : BaseEntity, IAggregateRoot
|
||||
public string Name { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public decimal Price { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
public int CatalogTypeId { get; private set; }
|
||||
public CatalogType CatalogType { get; private set; }
|
||||
public int CatalogBrandId { get; private set; }
|
||||
public CatalogBrand CatalogBrand { get; private set; }
|
||||
|
||||
public CatalogItem(int catalogTypeId,
|
||||
int catalogBrandId,
|
||||
string description,
|
||||
string name,
|
||||
decimal price,
|
||||
string pictureUri)
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public decimal Price { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
public int CatalogTypeId { get; private set; }
|
||||
public CatalogType CatalogType { get; private set; }
|
||||
public int CatalogBrandId { get; private set; }
|
||||
public CatalogBrand CatalogBrand { get; private set; }
|
||||
|
||||
public CatalogItem(int catalogTypeId,
|
||||
int catalogBrandId,
|
||||
string description,
|
||||
string name,
|
||||
decimal price,
|
||||
string pictureUri)
|
||||
{
|
||||
CatalogTypeId = catalogTypeId;
|
||||
CatalogBrandId = catalogBrandId;
|
||||
Description = description;
|
||||
Name = name;
|
||||
Price = price;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
public void UpdateDetails(string name, string description, decimal price)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(name, nameof(name));
|
||||
Guard.Against.NullOrEmpty(description, nameof(description));
|
||||
Guard.Against.NegativeOrZero(price, nameof(price));
|
||||
|
||||
Name = name;
|
||||
Description = description;
|
||||
Price = price;
|
||||
}
|
||||
|
||||
public void UpdateBrand(int catalogBrandId)
|
||||
{
|
||||
Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId));
|
||||
CatalogBrandId = catalogBrandId;
|
||||
}
|
||||
|
||||
public void UpdateType(int catalogTypeId)
|
||||
{
|
||||
Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId));
|
||||
CatalogTypeId = catalogTypeId;
|
||||
}
|
||||
|
||||
public void UpdatePictureUri(string pictureName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pictureName))
|
||||
{
|
||||
PictureUri = string.Empty;
|
||||
return;
|
||||
}
|
||||
PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}";
|
||||
}
|
||||
CatalogTypeId = catalogTypeId;
|
||||
CatalogBrandId = catalogBrandId;
|
||||
Description = description;
|
||||
Name = name;
|
||||
Price = price;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDetails(string name, string description, decimal price)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(name, nameof(name));
|
||||
Guard.Against.NullOrEmpty(description, nameof(description));
|
||||
Guard.Against.NegativeOrZero(price, nameof(price));
|
||||
|
||||
Name = name;
|
||||
Description = description;
|
||||
Price = price;
|
||||
}
|
||||
|
||||
public void UpdateBrand(int catalogBrandId)
|
||||
{
|
||||
Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId));
|
||||
CatalogBrandId = catalogBrandId;
|
||||
}
|
||||
|
||||
public void UpdateType(int catalogTypeId)
|
||||
{
|
||||
Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId));
|
||||
CatalogTypeId = catalogTypeId;
|
||||
}
|
||||
|
||||
public void UpdatePictureUri(string pictureName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pictureName))
|
||||
{
|
||||
PictureUri = string.Empty;
|
||||
return;
|
||||
}
|
||||
PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
public class CatalogType : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class CatalogType : BaseEntity, IAggregateRoot
|
||||
public string Type { get; private set; }
|
||||
public CatalogType(string type)
|
||||
{
|
||||
public string Type { get; private set; }
|
||||
public CatalogType(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class Address // ValueObject
|
||||
{
|
||||
public class Address // ValueObject
|
||||
public string Street { get; private set; }
|
||||
|
||||
public string City { get; private set; }
|
||||
|
||||
public string State { get; private set; }
|
||||
|
||||
public string Country { get; private set; }
|
||||
|
||||
public string ZipCode { get; private set; }
|
||||
|
||||
private Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
public string Street { get; private set; }
|
||||
|
||||
public string City { get; private set; }
|
||||
|
||||
public string State { get; private set; }
|
||||
|
||||
public string Country { get; private set; }
|
||||
|
||||
public string ZipCode { get; private set; }
|
||||
|
||||
private Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
using Ardalis.GuardClauses;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a snapshot of the item that was ordered. If catalog item details change, details of
|
||||
/// the item that was part of a completed order should not change.
|
||||
/// </summary>
|
||||
public class CatalogItemOrdered // ValueObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a snapshot of the item that was ordered. If catalog item details change, details of
|
||||
/// the item that was part of a completed order should not change.
|
||||
/// </summary>
|
||||
public class CatalogItemOrdered // ValueObject
|
||||
public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri)
|
||||
{
|
||||
public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri)
|
||||
{
|
||||
Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue);
|
||||
Guard.Against.NullOrEmpty(productName, nameof(productName));
|
||||
Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri));
|
||||
Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue);
|
||||
Guard.Against.NullOrEmpty(productName, nameof(productName));
|
||||
Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri));
|
||||
|
||||
CatalogItemId = catalogItemId;
|
||||
ProductName = productName;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
private CatalogItemOrdered()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public int CatalogItemId { get; private set; }
|
||||
public string ProductName { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
CatalogItemId = catalogItemId;
|
||||
ProductName = productName;
|
||||
PictureUri = pictureUri;
|
||||
}
|
||||
|
||||
private CatalogItemOrdered()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public int CatalogItemId { get; private set; }
|
||||
public string ProductName { get; private set; }
|
||||
public string PictureUri { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class Order : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public class Order : BaseEntity, IAggregateRoot
|
||||
private Order()
|
||||
{
|
||||
private Order()
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
|
||||
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
|
||||
Guard.Against.Null(items, nameof(items));
|
||||
|
||||
BuyerId = buyerId;
|
||||
ShipToAddress = shipToAddress;
|
||||
_orderItems = items;
|
||||
}
|
||||
|
||||
public string BuyerId { get; private set; }
|
||||
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
|
||||
public Address ShipToAddress { get; private set; }
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method Order.AddOrderItem() which includes behavior.
|
||||
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
|
||||
|
||||
// Using List<>.AsReadOnly()
|
||||
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
|
||||
|
||||
public decimal Total()
|
||||
{
|
||||
var total = 0m;
|
||||
foreach (var item in _orderItems)
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
|
||||
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
|
||||
Guard.Against.Null(items, nameof(items));
|
||||
|
||||
BuyerId = buyerId;
|
||||
ShipToAddress = shipToAddress;
|
||||
_orderItems = items;
|
||||
}
|
||||
|
||||
public string BuyerId { get; private set; }
|
||||
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
|
||||
public Address ShipToAddress { get; private set; }
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method Order.AddOrderItem() which includes behavior.
|
||||
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
|
||||
|
||||
// Using List<>.AsReadOnly()
|
||||
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
|
||||
|
||||
public decimal Total()
|
||||
{
|
||||
var total = 0m;
|
||||
foreach (var item in _orderItems)
|
||||
{
|
||||
total += item.UnitPrice * item.Units;
|
||||
}
|
||||
return total;
|
||||
total += item.UnitPrice * item.Units;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
public class OrderItem : BaseEntity
|
||||
{
|
||||
public class OrderItem : BaseEntity
|
||||
public CatalogItemOrdered ItemOrdered { get; private set; }
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Units { get; private set; }
|
||||
|
||||
private OrderItem()
|
||||
{
|
||||
public CatalogItemOrdered ItemOrdered { get; private set; }
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Units { get; private set; }
|
||||
// required by EF
|
||||
}
|
||||
|
||||
private OrderItem()
|
||||
{
|
||||
// required by EF
|
||||
}
|
||||
|
||||
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
|
||||
{
|
||||
ItemOrdered = itemOrdered;
|
||||
UnitPrice = unitPrice;
|
||||
Units = units;
|
||||
}
|
||||
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
|
||||
{
|
||||
ItemOrdered = itemOrdered;
|
||||
UnitPrice = unitPrice;
|
||||
Units = units;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class BasketNotFoundException : Exception
|
||||
{
|
||||
public class BasketNotFoundException : Exception
|
||||
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
|
||||
{
|
||||
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
{
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class DuplicateException : Exception
|
||||
public class DuplicateException : Exception
|
||||
{
|
||||
public DuplicateException(string message) : base(message)
|
||||
{
|
||||
public DuplicateException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
public class EmptyBasketOnCheckoutException : Exception
|
||||
{
|
||||
public class EmptyBasketOnCheckoutException : Exception
|
||||
public EmptyBasketOnCheckoutException()
|
||||
: base($"Basket cannot have 0 items on checkout")
|
||||
{
|
||||
public EmptyBasketOnCheckoutException()
|
||||
: base($"Basket cannot have 0 items on checkout")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
public EmptyBasketOnCheckoutException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
public EmptyBasketOnCheckoutException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
|
||||
|
||||
namespace Ardalis.GuardClauses
|
||||
namespace Ardalis.GuardClauses;
|
||||
|
||||
public static class BasketGuards
|
||||
{
|
||||
public static class BasketGuards
|
||||
public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket)
|
||||
{
|
||||
public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket)
|
||||
{
|
||||
if (basket == null)
|
||||
throw new BasketNotFoundException(basketId);
|
||||
}
|
||||
|
||||
public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection<BasketItem> basketItems)
|
||||
{
|
||||
if (!basketItems.Any())
|
||||
throw new EmptyBasketOnCheckoutException();
|
||||
}
|
||||
if (basket == null)
|
||||
throw new BasketNotFoundException(basketId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection<BasketItem> basketItems)
|
||||
{
|
||||
if (!basketItems.Any())
|
||||
throw new EmptyBasketOnCheckoutException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.eShopWeb
|
||||
namespace Microsoft.eShopWeb;
|
||||
|
||||
public static class JsonExtensions
|
||||
{
|
||||
public static class JsonExtensions
|
||||
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
public static T FromJson<T>(this string json) =>
|
||||
JsonSerializer.Deserialize<T>(json, _jsonOptions);
|
||||
public static T FromJson<T>(this string json) =>
|
||||
JsonSerializer.Deserialize<T>(json, _jsonOptions);
|
||||
|
||||
public static string ToJson<T>(this T obj) =>
|
||||
JsonSerializer.Serialize<T>(obj, _jsonOptions);
|
||||
}
|
||||
public static string ToJson<T>(this T obj) =>
|
||||
JsonSerializer.Serialize<T>(obj, _jsonOptions);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IAggregateRoot
|
||||
{ }
|
||||
}
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IAggregateRoot
|
||||
{ }
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IAppLogger<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IAppLogger<T>
|
||||
{
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogWarning(string message, params object[] args);
|
||||
}
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogWarning(string message, params object[] args);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IBasketQueryService
|
||||
{
|
||||
public interface IBasketQueryService
|
||||
{
|
||||
Task<int> CountTotalBasketItems(string username);
|
||||
}
|
||||
Task<int> CountTotalBasketItems(string username);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IBasketService
|
||||
{
|
||||
public interface IBasketService
|
||||
{
|
||||
Task TransferBasketAsync(string anonymousId, string userName);
|
||||
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);
|
||||
}
|
||||
Task TransferBasketAsync(string anonymousId, string userName);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
{
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendEmailAsync(string email, string subject, string message);
|
||||
}
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendEmailAsync(string email, string subject, string message);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IOrderService
|
||||
{
|
||||
public interface IOrderService
|
||||
{
|
||||
Task CreateOrderAsync(int basketId, Address shippingAddress);
|
||||
}
|
||||
Task CreateOrderAsync(int basketId, Address shippingAddress);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Ardalis.Specification;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Ardalis.Specification;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface ITokenClaimsService
|
||||
{
|
||||
public interface ITokenClaimsService
|
||||
{
|
||||
Task<string> GetTokenAsync(string userName);
|
||||
}
|
||||
Task<string> GetTokenAsync(string userName);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
public interface IUriComposer
|
||||
{
|
||||
public interface IUriComposer
|
||||
{
|
||||
string ComposePicUri(string uriTemplate);
|
||||
}
|
||||
string ComposePicUri(string uriTemplate);
|
||||
}
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class BasketService : IBasketService
|
||||
{
|
||||
public class BasketService : IBasketService
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IAppLogger<BasketService> _logger;
|
||||
|
||||
public BasketService(IRepository<Basket> basketRepository,
|
||||
IAppLogger<BasketService> logger)
|
||||
{
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IAppLogger<BasketService> _logger;
|
||||
_basketRepository = basketRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public BasketService(IRepository<Basket> basketRepository,
|
||||
IAppLogger<BasketService> logger)
|
||||
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(username);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
|
||||
if (basket == null)
|
||||
{
|
||||
_basketRepository = basketRepository;
|
||||
_logger = logger;
|
||||
basket = new Basket(username);
|
||||
await _basketRepository.AddAsync(basket);
|
||||
}
|
||||
|
||||
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(username);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
basket.AddItem(catalogItemId, price, quantity);
|
||||
|
||||
if (basket == null)
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task DeleteBasketAsync(int basketId)
|
||||
{
|
||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||
await _basketRepository.DeleteAsync(basket);
|
||||
}
|
||||
|
||||
public async Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities)
|
||||
{
|
||||
Guard.Against.Null(quantities, nameof(quantities));
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
|
||||
foreach (var item in basket.Items)
|
||||
{
|
||||
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
|
||||
{
|
||||
basket = new Basket(username);
|
||||
await _basketRepository.AddAsync(basket);
|
||||
if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
|
||||
item.SetQuantity(quantity);
|
||||
}
|
||||
|
||||
basket.AddItem(catalogItemId, price, quantity);
|
||||
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
basket.RemoveEmptyItems();
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task DeleteBasketAsync(int basketId)
|
||||
public async Task TransferBasketAsync(string anonymousId, string userName)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId));
|
||||
Guard.Against.NullOrEmpty(userName, nameof(userName));
|
||||
var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId);
|
||||
var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec);
|
||||
if (anonymousBasket == null) return;
|
||||
var userBasketSpec = new BasketWithItemsSpecification(userName);
|
||||
var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec);
|
||||
if (userBasket == null)
|
||||
{
|
||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||
await _basketRepository.DeleteAsync(basket);
|
||||
userBasket = new Basket(userName);
|
||||
await _basketRepository.AddAsync(userBasket);
|
||||
}
|
||||
|
||||
public async Task<Basket> SetQuantities(int basketId, Dictionary<string, int> quantities)
|
||||
foreach (var item in anonymousBasket.Items)
|
||||
{
|
||||
Guard.Against.Null(quantities, nameof(quantities));
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
|
||||
foreach (var item in basket.Items)
|
||||
{
|
||||
if (quantities.TryGetValue(item.Id.ToString(), out var quantity))
|
||||
{
|
||||
if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}.");
|
||||
item.SetQuantity(quantity);
|
||||
}
|
||||
}
|
||||
basket.RemoveEmptyItems();
|
||||
await _basketRepository.UpdateAsync(basket);
|
||||
return basket;
|
||||
}
|
||||
|
||||
public async Task TransferBasketAsync(string anonymousId, string userName)
|
||||
{
|
||||
Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId));
|
||||
Guard.Against.NullOrEmpty(userName, nameof(userName));
|
||||
var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId);
|
||||
var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec);
|
||||
if (anonymousBasket == null) return;
|
||||
var userBasketSpec = new BasketWithItemsSpecification(userName);
|
||||
var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec);
|
||||
if (userBasket == null)
|
||||
{
|
||||
userBasket = new Basket(userName);
|
||||
await _basketRepository.AddAsync(userBasket);
|
||||
}
|
||||
foreach (var item in anonymousBasket.Items)
|
||||
{
|
||||
userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity);
|
||||
}
|
||||
await _basketRepository.UpdateAsync(userBasket);
|
||||
await _basketRepository.DeleteAsync(anonymousBasket);
|
||||
userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity);
|
||||
}
|
||||
await _basketRepository.UpdateAsync(userBasket);
|
||||
await _basketRepository.DeleteAsync(anonymousBasket);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,53 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class OrderService : IOrderService
|
||||
{
|
||||
public class OrderService : IOrderService
|
||||
private readonly IRepository<Order> _orderRepository;
|
||||
private readonly IUriComposer _uriComposer;
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IRepository<CatalogItem> _itemRepository;
|
||||
|
||||
public OrderService(IRepository<Basket> basketRepository,
|
||||
IRepository<CatalogItem> itemRepository,
|
||||
IRepository<Order> orderRepository,
|
||||
IUriComposer uriComposer)
|
||||
{
|
||||
private readonly IRepository<Order> _orderRepository;
|
||||
private readonly IUriComposer _uriComposer;
|
||||
private readonly IRepository<Basket> _basketRepository;
|
||||
private readonly IRepository<CatalogItem> _itemRepository;
|
||||
_orderRepository = orderRepository;
|
||||
_uriComposer = uriComposer;
|
||||
_basketRepository = basketRepository;
|
||||
_itemRepository = itemRepository;
|
||||
}
|
||||
|
||||
public OrderService(IRepository<Basket> basketRepository,
|
||||
IRepository<CatalogItem> itemRepository,
|
||||
IRepository<Order> orderRepository,
|
||||
IUriComposer uriComposer)
|
||||
public async Task CreateOrderAsync(int basketId, Address shippingAddress)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
Guard.Against.EmptyBasketOnCheckout(basket.Items);
|
||||
|
||||
var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray());
|
||||
var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification);
|
||||
|
||||
var items = basket.Items.Select(basketItem =>
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
_uriComposer = uriComposer;
|
||||
_basketRepository = basketRepository;
|
||||
_itemRepository = itemRepository;
|
||||
}
|
||||
var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId);
|
||||
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
|
||||
var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity);
|
||||
return orderItem;
|
||||
}).ToList();
|
||||
|
||||
public async Task CreateOrderAsync(int basketId, Address shippingAddress)
|
||||
{
|
||||
var basketSpec = new BasketWithItemsSpecification(basketId);
|
||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
||||
var order = new Order(basket.BuyerId, shippingAddress, items);
|
||||
|
||||
Guard.Against.NullBasket(basketId, basket);
|
||||
Guard.Against.EmptyBasketOnCheckout(basket.Items);
|
||||
|
||||
var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray());
|
||||
var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification);
|
||||
|
||||
var items = basket.Items.Select(basketItem =>
|
||||
{
|
||||
var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId);
|
||||
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
|
||||
var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity);
|
||||
return orderItem;
|
||||
}).ToList();
|
||||
|
||||
var order = new Order(basket.BuyerId, shippingAddress, items);
|
||||
|
||||
await _orderRepository.AddAsync(order);
|
||||
}
|
||||
await _orderRepository.AddAsync(order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Services;
|
||||
|
||||
public class UriComposer : IUriComposer
|
||||
{
|
||||
public class UriComposer : IUriComposer
|
||||
private readonly CatalogSettings _catalogSettings;
|
||||
|
||||
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;
|
||||
|
||||
public string ComposePicUri(string uriTemplate)
|
||||
{
|
||||
private readonly CatalogSettings _catalogSettings;
|
||||
|
||||
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;
|
||||
|
||||
public string ComposePicUri(string uriTemplate)
|
||||
{
|
||||
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
|
||||
}
|
||||
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
{
|
||||
public sealed class BasketWithItemsSpecification : Specification<Basket>, ISingleResultSpecification
|
||||
{
|
||||
public BasketWithItemsSpecification(int basketId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.Id == basketId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public BasketWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.BuyerId == buyerId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
public sealed class BasketWithItemsSpecification : Specification<Basket>, ISingleResultSpecification
|
||||
{
|
||||
public BasketWithItemsSpecification(int basketId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.Id == basketId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
|
||||
public BasketWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query
|
||||
.Where(b => b.BuyerId == buyerId)
|
||||
.Include(b => b.Items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
|
||||
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
|
||||
: base()
|
||||
{
|
||||
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
|
||||
: base()
|
||||
if (take == 0)
|
||||
{
|
||||
if (take == 0)
|
||||
{
|
||||
take = int.MaxValue;
|
||||
}
|
||||
Query
|
||||
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId))
|
||||
.Skip(skip).Take(take);
|
||||
take = int.MaxValue;
|
||||
}
|
||||
Query
|
||||
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId))
|
||||
.Skip(skip).Take(take);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogFilterSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogFilterSpecification : Specification<CatalogItem>
|
||||
public CatalogFilterSpecification(int? brandId, int? typeId)
|
||||
{
|
||||
public CatalogFilterSpecification(int? brandId, int? typeId)
|
||||
{
|
||||
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId));
|
||||
}
|
||||
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
|
||||
(!typeId.HasValue || i.CatalogTypeId == typeId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogItemNameSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogItemNameSpecification : Specification<CatalogItem>
|
||||
public CatalogItemNameSpecification(string catalogItemName)
|
||||
{
|
||||
public CatalogItemNameSpecification(string catalogItemName)
|
||||
{
|
||||
Query.Where(item => catalogItemName == item.Name);
|
||||
}
|
||||
Query.Where(item => catalogItemName == item.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CatalogItemsSpecification : Specification<CatalogItem>
|
||||
{
|
||||
public class CatalogItemsSpecification : Specification<CatalogItem>
|
||||
public CatalogItemsSpecification(params int[] ids)
|
||||
{
|
||||
public CatalogItemsSpecification(params int[] ids)
|
||||
{
|
||||
Query.Where(c => ids.Contains(c.Id));
|
||||
}
|
||||
Query.Where(c => ids.Contains(c.Id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class CustomerOrdersWithItemsSpecification : Specification<Order>
|
||||
{
|
||||
public class CustomerOrdersWithItemsSpecification : Specification<Order>
|
||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||
{
|
||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||
{
|
||||
Query.Where(o => o.BuyerId == buyerId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
Query.Where(o => o.BuyerId == buyerId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Ardalis.Specification;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||
|
||||
public class OrderWithItemsByIdSpec : Specification<Order>, ISingleResultSpecification
|
||||
{
|
||||
public class OrderWithItemsByIdSpec : Specification<Order>, ISingleResultSpecification
|
||||
public OrderWithItemsByIdSpec(int orderId)
|
||||
{
|
||||
public OrderWithItemsByIdSpec(int orderId)
|
||||
{
|
||||
Query
|
||||
.Where(order => order.Id == orderId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
Query
|
||||
.Where(order => order.Id == orderId)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(i => i.ItemOrdered);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user