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

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

View File

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

View File

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

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

View File

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

View File

@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
namespace Infrastructure.Identity

View File

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

View File

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

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