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:
Steve Smith
2017-09-22 11:28:55 -04:00
committed by GitHub
parent b90bd08d11
commit aca618316a
70 changed files with 1755 additions and 513 deletions

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View 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));
}
}
}

View 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; }
}
}

View 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;
//}
}
}

View File

@@ -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; }
}
}

View 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;
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,5 @@
namespace ApplicationCore.Interfaces
{
public interface IAggregateRoot
{ }
}

View File

@@ -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);
}
}

View 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);
}
}

View File

@@ -1,7 +0,0 @@
namespace ApplicationCore.Interfaces
{
public interface IImageService
{
byte[] GetImageBytesById(int id);
}
}

View 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);
}
}

View 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);
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}
}