Initial Upgrade to .NET Core 2.0 (#50)
* Ardalis/upgrade1 (#44) * Upgrading to netcore 2.0 Updating repository to support async options and refactoring to use it. * Starting work on tracking customer orders feature. * Cleaning up some bugs Working on basket view component implementation * Fixing up styles, especially for basket in header. * Adding Order Features (#47) * Working on order model binding from checkout page - WIP * Small layout tweaks (#43) * Updating quantities implemented. * Fixed basket widget count * Order History (#49) * working on creating and viewing orders. * Working on wiring up listing of orders * List orders page works as expected. Needed to support ThenInclude scenarios. Currently using strings.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
26
src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs
Normal file
26
src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ApplicationCore.Entities.BuyerAggregate
|
||||
{
|
||||
public class Buyer : BaseEntity, IAggregateRoot
|
||||
{
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
protected Buyer()
|
||||
{
|
||||
}
|
||||
|
||||
public Buyer(string identity) : this()
|
||||
{
|
||||
IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
Normal file
11
src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace ApplicationCore.Entities.BuyerAggregate
|
||||
{
|
||||
public class PaymentMethod : BaseEntity
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
public string CardId { get; set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
public string Last4 { get; set; }
|
||||
}
|
||||
}
|
||||
39
src/ApplicationCore/Entities/OrderAggregate/Address.cs
Normal file
39
src/ApplicationCore/Entities/OrderAggregate/Address.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ApplicationCore.Entities.OrderAggregate
|
||||
{
|
||||
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)
|
||||
{
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
|
||||
//protected override IEnumerable<object> GetAtomicValues()
|
||||
//{
|
||||
// yield return Street;
|
||||
// yield return City;
|
||||
// yield return State;
|
||||
// yield return Country;
|
||||
// yield return ZipCode;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace ApplicationCore.Entities.OrderAggregate
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents 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)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
48
src/ApplicationCore/Entities/OrderAggregate/Order.cs
Normal file
48
src/ApplicationCore/Entities/OrderAggregate/Order.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using ApplicationCore.Interfaces;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ApplicationCore.Entities.OrderAggregate
|
||||
{
|
||||
public class Order : BaseEntity, IAggregateRoot
|
||||
{
|
||||
private Order()
|
||||
{
|
||||
}
|
||||
|
||||
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
|
||||
{
|
||||
ShipToAddress = shipToAddress;
|
||||
_orderItems = items;
|
||||
BuyerId = buyerId;
|
||||
}
|
||||
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>();
|
||||
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
|
||||
// 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 decimal Total()
|
||||
{
|
||||
var total = 0m;
|
||||
foreach (var item in _orderItems)
|
||||
{
|
||||
total += item.UnitPrice * item.Units;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
22
src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs
Normal file
22
src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
|
||||
namespace ApplicationCore.Entities.OrderAggregate
|
||||
{
|
||||
|
||||
public class OrderItem : BaseEntity
|
||||
{
|
||||
public CatalogItemOrdered ItemOrdered { get; private set; }
|
||||
public decimal UnitPrice { get; private set; }
|
||||
public int Units { get; private set; }
|
||||
|
||||
protected OrderItem()
|
||||
{
|
||||
}
|
||||
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
|
||||
{
|
||||
ItemOrdered = itemOrdered;
|
||||
UnitPrice = unitPrice;
|
||||
Units = units;
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/ApplicationCore/Interfaces/IAggregateRoot.cs
Normal file
5
src/ApplicationCore/Interfaces/IAggregateRoot.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IAggregateRoot
|
||||
{ }
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IAppLogger<T>
|
||||
{
|
||||
void LogInformation(string message, params object[] args);
|
||||
void LogWarning(string message, params object[] args);
|
||||
}
|
||||
}
|
||||
|
||||
16
src/ApplicationCore/Interfaces/IAsyncRepository.cs
Normal file
16
src/ApplicationCore/Interfaces/IAsyncRepository.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IAsyncRepository<T> where T : BaseEntity
|
||||
{
|
||||
Task<T> GetByIdAsync(int id);
|
||||
Task<List<T>> ListAllAsync();
|
||||
Task<List<T>> ListAsync(ISpecification<T> spec);
|
||||
Task<T> AddAsync(T entity);
|
||||
Task UpdateAsync(T entity);
|
||||
Task DeleteAsync(T entity);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IImageService
|
||||
{
|
||||
byte[] GetImageBytesById(int id);
|
||||
}
|
||||
}
|
||||
12
src/ApplicationCore/Interfaces/IOrderRepository.cs
Normal file
12
src/ApplicationCore/Interfaces/IOrderRepository.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
|
||||
public interface IOrderRepository : IRepository<Order>, IAsyncRepository<Order>
|
||||
{
|
||||
Order GetByIdWithItems(int id);
|
||||
Task<Order> GetByIdWithItemsAsync(int id);
|
||||
}
|
||||
}
|
||||
10
src/ApplicationCore/Interfaces/IOrderService.cs
Normal file
10
src/ApplicationCore/Interfaces/IOrderService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
public interface IOrderService
|
||||
{
|
||||
Task CreateOrderAsync(int basketId, Address shippingAddress);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
|
||||
public interface IRepository<T> where T : BaseEntity
|
||||
{
|
||||
T GetById(int id);
|
||||
List<T> List();
|
||||
List<T> List(ISpecification<T> spec);
|
||||
IEnumerable<T> ListAll();
|
||||
IEnumerable<T> List(ISpecification<T> spec);
|
||||
T Add(T entity);
|
||||
void Update(T entity);
|
||||
void Delete(T entity);
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ApplicationCore.Interfaces
|
||||
{
|
||||
Expression<Func<T, bool>> Criteria { get; }
|
||||
List<Expression<Func<T, object>>> Includes { get; }
|
||||
List<string> IncludeStrings { get; }
|
||||
void AddInclude(Expression<Func<T, object>> includeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ApplicationCore.Interfaces
|
||||
namespace ApplicationCore.Interfaces
|
||||
{
|
||||
|
||||
public interface IUriComposer
|
||||
{
|
||||
string ComposePicUri(string uriTemplate);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Collections.Generic;
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace ApplicationCore.Specifications
|
||||
{
|
||||
@@ -28,6 +29,8 @@ namespace ApplicationCore.Specifications
|
||||
|
||||
public List<Expression<Func<Basket, object>>> Includes { get; } = new List<Expression<Func<Basket, object>>>();
|
||||
|
||||
public List<string> IncludeStrings { get; } = new List<string>();
|
||||
|
||||
public void AddInclude(Expression<Func<Basket, object>> includeExpression)
|
||||
{
|
||||
Includes.Add(includeExpression);
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace ApplicationCore.Specifications
|
||||
|
||||
public List<Expression<Func<CatalogItem, object>>> Includes { get; } = new List<Expression<Func<CatalogItem, object>>>();
|
||||
|
||||
public List<string> IncludeStrings { get; } = new List<string>();
|
||||
|
||||
public void AddInclude(Expression<Func<CatalogItem, object>> includeExpression)
|
||||
{
|
||||
Includes.Add(includeExpression);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using ApplicationCore.Interfaces;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Collections.Generic;
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace ApplicationCore.Specifications
|
||||
{
|
||||
public class CustomerOrdersWithItemsSpecification : ISpecification<Order>
|
||||
{
|
||||
private readonly string _buyerId;
|
||||
|
||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||
{
|
||||
_buyerId = buyerId;
|
||||
AddInclude(o => o.OrderItems);
|
||||
AddInclude("OrderItems.ItemOrdered");
|
||||
}
|
||||
|
||||
public Expression<Func<Order, bool>> Criteria => o => o.BuyerId == _buyerId;
|
||||
|
||||
public List<Expression<Func<Order, object>>> Includes { get; } = new List<Expression<Func<Order, object>>>();
|
||||
public List<string> IncludeStrings { get; } = new List<string>();
|
||||
|
||||
public void AddInclude(Expression<Func<Order, object>> includeExpression)
|
||||
{
|
||||
Includes.Add(includeExpression);
|
||||
}
|
||||
|
||||
public void AddInclude(string includeString)
|
||||
{
|
||||
IncludeStrings.Add(includeString);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user