From 964173f27758f3be72bf8c3b1aea7db54e8f507f Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Wed, 18 Oct 2017 13:18:25 -0400 Subject: [PATCH] Using Base Specification Class (#56) * 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. * Remove non-icon basket link from header Add comments to EF query logic * Refactoring to use base specification type * minor cleanup --- .../Interfaces/ISpecification.cs | 1 - .../Specifications/BaseSpecification.cs | 27 +++++++++++++++++ .../BasketWithItemsSpecification.cs | 29 +++---------------- .../CatalogFilterSpecification.cs | 28 +++--------------- .../CustomerOrdersWithItemsSpecification.cs | 27 ++--------------- src/Infrastructure/Data/EfRepository.cs | 9 ++++++ src/Web/Views/Shared/_Layout.cshtml | 2 -- 7 files changed, 47 insertions(+), 76 deletions(-) create mode 100644 src/ApplicationCore/Specifications/BaseSpecification.cs diff --git a/src/ApplicationCore/Interfaces/ISpecification.cs b/src/ApplicationCore/Interfaces/ISpecification.cs index 5b1173486..f9a216712 100644 --- a/src/ApplicationCore/Interfaces/ISpecification.cs +++ b/src/ApplicationCore/Interfaces/ISpecification.cs @@ -9,6 +9,5 @@ public interface ISpecification Expression> Criteria { get; } List>> Includes { get; } List IncludeStrings { get; } - void AddInclude(Expression> includeExpression); } } diff --git a/src/ApplicationCore/Specifications/BaseSpecification.cs b/src/ApplicationCore/Specifications/BaseSpecification.cs new file mode 100644 index 000000000..e39a86d6f --- /dev/null +++ b/src/ApplicationCore/Specifications/BaseSpecification.cs @@ -0,0 +1,27 @@ +using ApplicationCore.Interfaces; +using System; +using System.Linq.Expressions; +using System.Collections.Generic; + +namespace ApplicationCore.Specifications +{ + public abstract class BaseSpecification : ISpecification + { + public BaseSpecification(Expression> criteria) + { + Criteria = criteria; + } + public Expression> Criteria { get; } + public List>> Includes { get; } = new List>>(); + public List IncludeStrings { get; } = new List(); + + protected virtual void AddInclude(Expression> includeExpression) + { + Includes.Add(includeExpression); + } + protected virtual void AddInclude(string includeString) + { + IncludeStrings.Add(includeString); + } + } +} diff --git a/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs b/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs index a44fb7766..5608dc6a0 100644 --- a/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs +++ b/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs @@ -1,39 +1,18 @@ -using ApplicationCore.Interfaces; -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System; -using System.Linq.Expressions; -using System.Collections.Generic; -using ApplicationCore.Entities.OrderAggregate; +using Microsoft.eShopWeb.ApplicationCore.Entities; namespace ApplicationCore.Specifications { - public class BasketWithItemsSpecification : ISpecification + public class BasketWithItemsSpecification : BaseSpecification { public BasketWithItemsSpecification(int basketId) + :base(b => b.Id == basketId) { - BasketId = basketId; AddInclude(b => b.Items); } public BasketWithItemsSpecification(string buyerId) + :base(b => b.BuyerId == buyerId) { - BuyerId = buyerId; AddInclude(b => b.Items); } - - public int? BasketId { get; } - public string BuyerId { get; } - - public Expression> Criteria => b => - (BasketId.HasValue && b.Id == BasketId.Value) - || (BuyerId != null && b.BuyerId == BuyerId); - - public List>> Includes { get; } = new List>>(); - - public List IncludeStrings { get; } = new List(); - - public void AddInclude(Expression> includeExpression) - { - Includes.Add(includeExpression); - } } } diff --git a/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs b/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs index 54e1f8628..efcba8c5d 100644 --- a/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs @@ -1,34 +1,14 @@ -using ApplicationCore.Interfaces; -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System; -using System.Linq.Expressions; -using System.Collections.Generic; +using Microsoft.eShopWeb.ApplicationCore.Entities; namespace ApplicationCore.Specifications { - public class CatalogFilterSpecification : ISpecification + public class CatalogFilterSpecification : BaseSpecification { public CatalogFilterSpecification(int? brandId, int? typeId) + : base(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && + (!typeId.HasValue || i.CatalogTypeId == typeId)) { - BrandId = brandId; - TypeId = typeId; - } - - public int? BrandId { get; } - public int? TypeId { get; } - - public Expression> Criteria => - i => (!BrandId.HasValue || i.CatalogBrandId == BrandId) && - (!TypeId.HasValue || i.CatalogTypeId == TypeId); - - public List>> Includes { get; } = new List>>(); - - public List IncludeStrings { get; } = new List(); - - public void AddInclude(Expression> includeExpression) - { - Includes.Add(includeExpression); } } } diff --git a/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs b/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs index abfa9b658..df234c418 100644 --- a/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs +++ b/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs @@ -1,35 +1,14 @@ -using ApplicationCore.Interfaces; -using System; -using System.Linq.Expressions; -using System.Collections.Generic; -using ApplicationCore.Entities.OrderAggregate; +using ApplicationCore.Entities.OrderAggregate; namespace ApplicationCore.Specifications { - public class CustomerOrdersWithItemsSpecification : ISpecification + public class CustomerOrdersWithItemsSpecification : BaseSpecification { - private readonly string _buyerId; - public CustomerOrdersWithItemsSpecification(string buyerId) + : base(o => o.BuyerId == buyerId) { - _buyerId = buyerId; AddInclude(o => o.OrderItems); AddInclude("OrderItems.ItemOrdered"); } - - public Expression> Criteria => o => o.BuyerId == _buyerId; - - public List>> Includes { get; } = new List>>(); - public List IncludeStrings { get; } = new List(); - - public void AddInclude(Expression> includeExpression) - { - Includes.Add(includeExpression); - } - - public void AddInclude(string includeString) - { - IncludeStrings.Add(includeString); - } } } diff --git a/src/Infrastructure/Data/EfRepository.cs b/src/Infrastructure/Data/EfRepository.cs index cdc84fa44..b0cecfa10 100644 --- a/src/Infrastructure/Data/EfRepository.cs +++ b/src/Infrastructure/Data/EfRepository.cs @@ -43,25 +43,34 @@ public async Task> ListAllAsync() public IEnumerable List(ISpecification spec) { + // fetch a Queryable that includes all expression-based includes var queryableResultWithIncludes = spec.Includes .Aggregate(_dbContext.Set().AsQueryable(), (current, include) => current.Include(include)); + + // modify the IQueryable to include any string-based include statements var secondaryResult = spec.IncludeStrings .Aggregate(queryableResultWithIncludes, (current, include) => current.Include(include)); + + // return the result of the query using the specification's criteria expression return secondaryResult .Where(spec.Criteria) .AsEnumerable(); } public async Task> ListAsync(ISpecification spec) { + // fetch a Queryable that includes all expression-based includes var queryableResultWithIncludes = spec.Includes .Aggregate(_dbContext.Set().AsQueryable(), (current, include) => current.Include(include)); + + // modify the IQueryable to include any string-based include statements var secondaryResult = spec.IncludeStrings .Aggregate(queryableResultWithIncludes, (current, include) => current.Include(include)); + // return the result of the query using the specification's criteria expression return await secondaryResult .Where(spec.Criteria) .ToListAsync(); diff --git a/src/Web/Views/Shared/_Layout.cshtml b/src/Web/Views/Shared/_Layout.cshtml index 998deb2c4..8803d103c 100644 --- a/src/Web/Views/Shared/_Layout.cshtml +++ b/src/Web/Views/Shared/_Layout.cshtml @@ -33,8 +33,6 @@ @await Html.PartialAsync("_LoginPartial") -
Basket
-