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:
@@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
|
||||
namespace Infrastructure.Data
|
||||
{
|
||||
@@ -20,6 +21,8 @@ namespace Infrastructure.Data
|
||||
public DbSet<CatalogItem> CatalogItems { get; set; }
|
||||
public DbSet<CatalogBrand> CatalogBrands { get; set; }
|
||||
public DbSet<CatalogType> CatalogTypes { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<OrderItem> OrderItems { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
@@ -27,6 +30,8 @@ namespace Infrastructure.Data
|
||||
builder.Entity<CatalogBrand>(ConfigureCatalogBrand);
|
||||
builder.Entity<CatalogType>(ConfigureCatalogType);
|
||||
builder.Entity<CatalogItem>(ConfigureCatalogItem);
|
||||
builder.Entity<Order>(ConfigureOrder);
|
||||
builder.Entity<OrderItem>(ConfigureOrderItem);
|
||||
}
|
||||
|
||||
private void ConfigureBasket(EntityTypeBuilder<Basket> builder)
|
||||
@@ -36,7 +41,7 @@ namespace Infrastructure.Data
|
||||
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
|
||||
}
|
||||
|
||||
void ConfigureCatalogItem(EntityTypeBuilder<CatalogItem> builder)
|
||||
private void ConfigureCatalogItem(EntityTypeBuilder<CatalogItem> builder)
|
||||
{
|
||||
builder.ToTable("Catalog");
|
||||
|
||||
@@ -63,7 +68,7 @@ namespace Infrastructure.Data
|
||||
.HasForeignKey(ci => ci.CatalogTypeId);
|
||||
}
|
||||
|
||||
void ConfigureCatalogBrand(EntityTypeBuilder<CatalogBrand> builder)
|
||||
private void ConfigureCatalogBrand(EntityTypeBuilder<CatalogBrand> builder)
|
||||
{
|
||||
builder.ToTable("CatalogBrand");
|
||||
|
||||
@@ -78,7 +83,7 @@ namespace Infrastructure.Data
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
|
||||
void ConfigureCatalogType(EntityTypeBuilder<CatalogType> builder)
|
||||
private void ConfigureCatalogType(EntityTypeBuilder<CatalogType> builder)
|
||||
{
|
||||
builder.ToTable("CatalogType");
|
||||
|
||||
@@ -92,5 +97,14 @@ namespace Infrastructure.Data
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
}
|
||||
private void ConfigureOrder(EntityTypeBuilder<Order> builder)
|
||||
{
|
||||
builder.OwnsOne(o => o.ShipToAddress);
|
||||
}
|
||||
private void ConfigureOrderItem(EntityTypeBuilder<OrderItem> builder)
|
||||
{
|
||||
builder.OwnsOne(i => i.ItemOrdered);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,39 +10,38 @@ namespace Infrastructure.Data
|
||||
{
|
||||
public class CatalogContextSeed
|
||||
{
|
||||
public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0)
|
||||
public static async Task SeedAsync(IApplicationBuilder applicationBuilder,
|
||||
CatalogContext catalogContext,
|
||||
ILoggerFactory loggerFactory, int? retry = 0)
|
||||
{
|
||||
int retryForAvailability = retry.Value;
|
||||
try
|
||||
{
|
||||
var context = (CatalogContext)applicationBuilder
|
||||
.ApplicationServices.GetService(typeof(CatalogContext));
|
||||
|
||||
// TODO: Only run this if using a real database
|
||||
// context.Database.Migrate();
|
||||
|
||||
if (!context.CatalogBrands.Any())
|
||||
if (!catalogContext.CatalogBrands.Any())
|
||||
{
|
||||
context.CatalogBrands.AddRange(
|
||||
catalogContext.CatalogBrands.AddRange(
|
||||
GetPreconfiguredCatalogBrands());
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!context.CatalogTypes.Any())
|
||||
if (!catalogContext.CatalogTypes.Any())
|
||||
{
|
||||
context.CatalogTypes.AddRange(
|
||||
catalogContext.CatalogTypes.AddRange(
|
||||
GetPreconfiguredCatalogTypes());
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!context.CatalogItems.Any())
|
||||
if (!catalogContext.CatalogItems.Any())
|
||||
{
|
||||
context.CatalogItems.AddRange(
|
||||
catalogContext.CatalogItems.AddRange(
|
||||
GetPreconfiguredItems());
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
await catalogContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -50,9 +49,9 @@ namespace Infrastructure.Data
|
||||
if (retryForAvailability < 10)
|
||||
{
|
||||
retryForAvailability++;
|
||||
var log = loggerFactory.CreateLogger("catalog seed");
|
||||
var log = loggerFactory.CreateLogger<CatalogContextSeed>();
|
||||
log.LogError(ex.Message);
|
||||
await SeedAsync(applicationBuilder, loggerFactory, retryForAvailability);
|
||||
await SeedAsync(applicationBuilder, catalogContext, loggerFactory, retryForAvailability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,36 +3,68 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Infrastructure.Data
|
||||
{
|
||||
public class EfRepository<T> : IRepository<T> where T : BaseEntity
|
||||
/// <summary>
|
||||
/// "There's some repetition here - couldn't we have some the sync methods call the async?"
|
||||
/// https://blogs.msdn.microsoft.com/pfxteam/2012/04/13/should-i-expose-synchronous-wrappers-for-asynchronous-methods/
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class EfRepository<T> : IRepository<T>, IAsyncRepository<T> where T : BaseEntity
|
||||
{
|
||||
private readonly CatalogContext _dbContext;
|
||||
protected readonly CatalogContext _dbContext;
|
||||
|
||||
public EfRepository(CatalogContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public T GetById(int id)
|
||||
public virtual T GetById(int id)
|
||||
{
|
||||
return _dbContext.Set<T>().SingleOrDefault(e => e.Id == id);
|
||||
return _dbContext.Set<T>().Find(id);
|
||||
}
|
||||
|
||||
public List<T> List()
|
||||
public virtual async Task<T> GetByIdAsync(int id)
|
||||
{
|
||||
return _dbContext.Set<T>().ToList();
|
||||
return await _dbContext.Set<T>().FindAsync(id);
|
||||
}
|
||||
|
||||
public List<T> List(ISpecification<T> spec)
|
||||
public IEnumerable<T> ListAll()
|
||||
{
|
||||
return _dbContext.Set<T>().AsEnumerable();
|
||||
}
|
||||
|
||||
public async Task<List<T>> ListAllAsync()
|
||||
{
|
||||
return await _dbContext.Set<T>().ToListAsync();
|
||||
}
|
||||
|
||||
public IEnumerable<T> List(ISpecification<T> spec)
|
||||
{
|
||||
var queryableResultWithIncludes = spec.Includes
|
||||
.Aggregate(_dbContext.Set<T>().AsQueryable(),
|
||||
(current, include) => current.Include(include));
|
||||
return queryableResultWithIncludes
|
||||
.Aggregate(_dbContext.Set<T>().AsQueryable(),
|
||||
(current, include) => current.Include(include));
|
||||
var secondaryResult = spec.IncludeStrings
|
||||
.Aggregate(queryableResultWithIncludes,
|
||||
(current, include) => current.Include(include));
|
||||
return secondaryResult
|
||||
.Where(spec.Criteria)
|
||||
.ToList();
|
||||
.AsEnumerable();
|
||||
}
|
||||
public async Task<List<T>> ListAsync(ISpecification<T> spec)
|
||||
{
|
||||
var queryableResultWithIncludes = spec.Includes
|
||||
.Aggregate(_dbContext.Set<T>().AsQueryable(),
|
||||
(current, include) => current.Include(include));
|
||||
var secondaryResult = spec.IncludeStrings
|
||||
.Aggregate(queryableResultWithIncludes,
|
||||
(current, include) => current.Include(include));
|
||||
|
||||
return await secondaryResult
|
||||
.Where(spec.Criteria)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public T Add(T entity)
|
||||
@@ -43,10 +75,12 @@ namespace Infrastructure.Data
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void Delete(T entity)
|
||||
public async Task<T> AddAsync(T entity)
|
||||
{
|
||||
_dbContext.Set<T>().Remove(entity);
|
||||
_dbContext.SaveChanges();
|
||||
_dbContext.Set<T>().Add(entity);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void Update(T entity)
|
||||
@@ -54,6 +88,21 @@ namespace Infrastructure.Data
|
||||
_dbContext.Entry(entity).State = EntityState.Modified;
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
public async Task UpdateAsync(T entity)
|
||||
{
|
||||
_dbContext.Entry(entity).State = EntityState.Modified;
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public void Delete(T entity)
|
||||
{
|
||||
_dbContext.Set<T>().Remove(entity);
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
public async Task DeleteAsync(T entity)
|
||||
{
|
||||
_dbContext.Set<T>().Remove(entity);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
src/Infrastructure/Data/OrderRepository.cs
Normal file
31
src/Infrastructure/Data/OrderRepository.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
using ApplicationCore.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Infrastructure.Data
|
||||
{
|
||||
public class OrderRepository : EfRepository<Order>, IOrderRepository
|
||||
{
|
||||
public OrderRepository(CatalogContext dbContext) : base(dbContext)
|
||||
{
|
||||
}
|
||||
|
||||
public Order GetByIdWithItems(int id)
|
||||
{
|
||||
return _dbContext.Orders
|
||||
.Include(o => o.OrderItems)
|
||||
.Include("OrderItems.ItemOrdered")
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public Task<Order> GetByIdWithItemsAsync(int id)
|
||||
{
|
||||
return _dbContext.Orders
|
||||
.Include(o => o.OrderItems)
|
||||
.Include("OrderItems.ItemOrdered")
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using ApplicationCore.Exceptions;
|
||||
using ApplicationCore.Interfaces;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.IO;
|
||||
|
||||
namespace Infrastructure.FileSystem
|
||||
{
|
||||
public class LocalFileImageService : IImageService
|
||||
{
|
||||
private readonly IHostingEnvironment _env;
|
||||
|
||||
public LocalFileImageService(IHostingEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
}
|
||||
public byte[] GetImageBytesById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var contentRoot = _env.ContentRootPath + "//Pics";
|
||||
var path = Path.Combine(contentRoot, id + ".png");
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
throw new CatalogImageMissingException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
|
||||
namespace Infrastructure.Identity
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
|
||||
<PackageReference Include="StructureMap.Microsoft.DependencyInjection" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\Migrations\" />
|
||||
<Folder Include="Services\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -10,9 +10,14 @@ namespace Infrastructure.Logging
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<T>();
|
||||
}
|
||||
|
||||
public void LogWarning(string message, params object[] args)
|
||||
{
|
||||
_logger.LogWarning(message, args);
|
||||
}
|
||||
public void LogInformation(string message, params object[] args)
|
||||
{
|
||||
_logger.LogInformation(message, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
src/Infrastructure/Services/OrderService.cs
Normal file
40
src/Infrastructure/Services/OrderService.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using ApplicationCore.Interfaces;
|
||||
using ApplicationCore.Entities.OrderAggregate;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Infrastructure.Services
|
||||
{
|
||||
public class OrderService : IOrderService
|
||||
{
|
||||
private readonly IAsyncRepository<Order> _orderRepository;
|
||||
private readonly IAsyncRepository<Basket> _basketRepository;
|
||||
private readonly IAsyncRepository<CatalogItem> _itemRepository;
|
||||
|
||||
public OrderService(IAsyncRepository<Basket> basketRepository,
|
||||
IAsyncRepository<CatalogItem> itemRepository,
|
||||
IAsyncRepository<Order> orderRepository)
|
||||
{
|
||||
_orderRepository = orderRepository;
|
||||
_basketRepository = basketRepository;
|
||||
_itemRepository = itemRepository;
|
||||
}
|
||||
|
||||
public async Task CreateOrderAsync(int basketId, Address shippingAddress)
|
||||
{
|
||||
var basket = await _basketRepository.GetByIdAsync(basketId);
|
||||
var items = new List<OrderItem>();
|
||||
foreach (var item in basket.Items)
|
||||
{
|
||||
var catalogItem = await _itemRepository.GetByIdAsync(item.CatalogItemId);
|
||||
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, catalogItem.PictureUri);
|
||||
var orderItem = new OrderItem(itemOrdered, item.UnitPrice, item.Quantity);
|
||||
items.Add(orderItem);
|
||||
}
|
||||
var order = new Order(basket.BuyerId, shippingAddress, items);
|
||||
|
||||
await _orderRepository.AddAsync(order);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user