Browse Source

Upgrade to use Specification 4.0.0 (#444)

main
Steve Smith 5 years ago
committed by GitHub
parent
commit
754c845e9f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/ApplicationCore/ApplicationCore.csproj
  2. 14
      src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
  3. 10
      src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
  4. 7
      src/ApplicationCore/Specifications/CatalogFilterSpecification.cs
  5. 6
      src/ApplicationCore/Specifications/CatalogItemsSpecification.cs
  6. 9
      src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs
  7. 14
      src/Infrastructure/Data/EfRepository.cs
  8. 3
      src/Infrastructure/Infrastructure.csproj
  9. 2
      src/Web/Areas/Identity/Pages/Account/Login.cshtml
  10. 2
      src/Web/Web.csproj
  11. 30
      tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
  12. 4
      tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
  13. 4
      tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs
  14. 4
      tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs
  15. 4
      tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs

2
src/ApplicationCore/ApplicationCore.csproj

@ -7,7 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="1.5.0" /> <PackageReference Include="Ardalis.GuardClauses" Version="1.5.0" />
<PackageReference Include="Ardalis.Specification" Version="3.0.0" /> <PackageReference Include="Ardalis.Specification" Version="4.0.0" />
<PackageReference Include="MediatR" Version="8.0.2" /> <PackageReference Include="MediatR" Version="8.0.2" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" /> <PackageReference Include="System.Security.Claims" Version="4.3.0" />
<PackageReference Include="System.Text.Json" Version="4.7.2" /> <PackageReference Include="System.Text.Json" Version="4.7.2" />

14
src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs

@ -3,16 +3,20 @@ using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{ {
public sealed class BasketWithItemsSpecification : BaseSpecification<Basket> public sealed class BasketWithItemsSpecification : Specification<Basket>
{ {
public BasketWithItemsSpecification(int basketId) : base(b => b.Id == basketId) public BasketWithItemsSpecification(int basketId)
{ {
AddInclude(b => b.Items); Query
.Where(b => b.Id == basketId)
.Include(b => b.Items);
} }
public BasketWithItemsSpecification(string buyerId) : base(b => b.BuyerId == buyerId) public BasketWithItemsSpecification(string buyerId)
{ {
AddInclude(b => b.Items); Query
.Where(b => b.BuyerId == buyerId)
.Include(b => b.Items);
} }
} }
} }

10
src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs

@ -3,13 +3,15 @@ using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{ {
public class CatalogFilterPaginatedSpecification : BaseSpecification<CatalogItem> public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
{ {
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId) public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
: base(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && : base()
(!typeId.HasValue || i.CatalogTypeId == typeId))
{ {
ApplyPaging(skip, take); Query
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId))
.Paginate(skip, take);
} }
} }
} }

7
src/ApplicationCore/Specifications/CatalogFilterSpecification.cs

@ -3,13 +3,12 @@ using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{ {
public class CatalogFilterSpecification : Specification<CatalogItem>
public class CatalogFilterSpecification : BaseSpecification<CatalogItem>
{ {
public CatalogFilterSpecification(int? brandId, int? typeId) public CatalogFilterSpecification(int? brandId, int? typeId)
: base(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId))
{ {
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId));
} }
} }
} }

6
src/ApplicationCore/Specifications/CatalogItemsSpecification.cs

@ -5,11 +5,11 @@ using System.Linq;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{ {
public class CatalogItemsSpecification : BaseSpecification<CatalogItem> public class CatalogItemsSpecification : Specification<CatalogItem>
{ {
public CatalogItemsSpecification(params int[] ids) : base(c => ids.Contains(c.Id)) public CatalogItemsSpecification(params int[] ids)
{ {
Query.Where(c => ids.Contains(c.Id));
} }
} }
} }

9
src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs

@ -1,16 +1,15 @@
using Ardalis.Specification; using Ardalis.Specification;
using Ardalis.Specification.QueryExtensions.Include;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{ {
public class CustomerOrdersWithItemsSpecification : BaseSpecification<Order> public class CustomerOrdersWithItemsSpecification : Specification<Order>
{ {
public CustomerOrdersWithItemsSpecification(string buyerId) public CustomerOrdersWithItemsSpecification(string buyerId)
: base(o => o.BuyerId == buyerId)
{ {
AddIncludes(query => query.Include(o => o.OrderItems) Query.Where(o => o.BuyerId == buyerId)
.ThenInclude(i => i.ItemOrdered)); .Include(o => o.OrderItems)
.ThenInclude(i => i.ItemOrdered);
} }
} }
} }

14
src/Infrastructure/Data/EfRepository.cs

@ -1,4 +1,5 @@
using Ardalis.Specification; using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Interfaces;
@ -34,13 +35,13 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec) public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec)
{ {
var specificationResult = await ApplySpecification(spec); var specificationResult = ApplySpecification(spec);
return await specificationResult.ToListAsync(); return await specificationResult.ToListAsync();
} }
public async Task<int> CountAsync(ISpecification<T> spec) public async Task<int> CountAsync(ISpecification<T> spec)
{ {
var specificationResult = await ApplySpecification(spec); var specificationResult = ApplySpecification(spec);
return await specificationResult.CountAsync(); return await specificationResult.CountAsync();
} }
@ -66,19 +67,20 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
public async Task<T> FirstAsync(ISpecification<T> spec) public async Task<T> FirstAsync(ISpecification<T> spec)
{ {
var specificationResult = await ApplySpecification(spec); var specificationResult = ApplySpecification(spec);
return await specificationResult.FirstAsync(); return await specificationResult.FirstAsync();
} }
public async Task<T> FirstOrDefaultAsync(ISpecification<T> spec) public async Task<T> FirstOrDefaultAsync(ISpecification<T> spec)
{ {
var specificationResult = await ApplySpecification(spec); var specificationResult = ApplySpecification(spec);
return await specificationResult.FirstOrDefaultAsync(); return await specificationResult.FirstOrDefaultAsync();
} }
private async Task<IQueryable<T>> ApplySpecification(ISpecification<T> spec) private IQueryable<T> ApplySpecification(ISpecification<T> spec)
{ {
return await EfSpecificationEvaluator<T>.GetQuery(_dbContext.Set<T>().AsQueryable(), spec); var evaluator = new SpecificationEvaluator<T>();
return evaluator.GetQuery(_dbContext.Set<T>().AsQueryable(), spec);
} }
} }
} }

3
src/Infrastructure/Infrastructure.csproj

@ -7,7 +7,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.EFCore.Extensions" Version="1.1.0" /> <PackageReference Include="Ardalis.EFCore.Extensions" Version="1.1.0" />
<PackageReference Include="Ardalis.Specification" Version="3.0.0" /> <PackageReference Include="Ardalis.Specification" Version="4.0.0" />
<PackageReference Include="Ardalis.Specification.EntityFrameworkCore" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.5" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.5" />

2
src/Web/Areas/Identity/Pages/Account/Login.cshtml

@ -47,7 +47,7 @@
Note that for demo purposes you don't need to register and can login with these credentials: Note that for demo purposes you don't need to register and can login with these credentials:
</p> </p>
<p> <p>
User: <b>demouser@microsoft.com</b> User: <b>demouser@microsoft.com</b> OR <b>admin@microsoft.com</b>
</p> </p>
<p> <p>
Password: <b>Pass@word1</b> Password: <b>Pass@word1</b>

2
src/Web/Web.csproj

@ -22,7 +22,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.ApiEndpoints" Version="1.0.0" /> <PackageReference Include="Ardalis.ApiEndpoints" Version="1.0.0" />
<PackageReference Include="Ardalis.ListStartupServices" Version="1.1.3" /> <PackageReference Include="Ardalis.ListStartupServices" Version="1.1.3" />
<PackageReference Include="Ardalis.Specification" Version="3.0.0" /> <PackageReference Include="Ardalis.Specification" Version="4.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="MediatR" Version="8.0.2" /> <PackageReference Include="MediatR" Version="8.0.2" />

30
tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs

@ -4,22 +4,25 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
using Moq; using Moq;
using Ardalis.Specification.EntityFrameworkCore;
namespace Microsoft.eShopWeb.UnitTests namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
{ {
public class BasketWithItems public class BasketWithItems
{ {
private readonly int _testBasketId = 123; private readonly int _testBasketId = 123;
private readonly string _buyerId = "Test buyerId"; private readonly string _buyerId = "Test buyerId";
// tests with specifications can use an evaluator or just WhereExpressions.FirstOrDefault if only one
private readonly SpecificationEvaluator<Basket> _evaluator = new SpecificationEvaluator<Basket>();
[Fact] [Fact]
public void MatchesBasketWithGivenBasketId() public void MatchesBasketWithGivenBasketId()
{ {
var spec = new BasketWithItemsSpecification(_testBasketId); var spec = new BasketWithItemsSpecification(_testBasketId);
var result = GetTestBasketCollection() var result = _evaluator.GetQuery(GetTestBasketCollection().AsQueryable(), spec)
.AsQueryable() .FirstOrDefault();
.FirstOrDefault(spec.Criterias.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(_testBasketId, result.Id); Assert.Equal(_testBasketId, result.Id);
@ -31,9 +34,10 @@ namespace Microsoft.eShopWeb.UnitTests
int badBasketId = -1; int badBasketId = -1;
var spec = new BasketWithItemsSpecification(badBasketId); var spec = new BasketWithItemsSpecification(badBasketId);
Assert.False(GetTestBasketCollection() var result = _evaluator.GetQuery(GetTestBasketCollection().AsQueryable(), spec)
.AsQueryable() .Any();
.Any(spec.Criterias.FirstOrDefault()));
Assert.False(result);
} }
[Fact] [Fact]
@ -41,9 +45,8 @@ namespace Microsoft.eShopWeb.UnitTests
{ {
var spec = new BasketWithItemsSpecification(_buyerId); var spec = new BasketWithItemsSpecification(_buyerId);
var result = GetTestBasketCollection() var result = _evaluator.GetQuery(GetTestBasketCollection().AsQueryable(), spec)
.AsQueryable() .FirstOrDefault();
.FirstOrDefault(spec.Criterias.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(_buyerId, result.BuyerId); Assert.Equal(_buyerId, result.BuyerId);
@ -55,9 +58,10 @@ namespace Microsoft.eShopWeb.UnitTests
string badBuyerId = "badBuyerId"; string badBuyerId = "badBuyerId";
var spec = new BasketWithItemsSpecification(badBuyerId); var spec = new BasketWithItemsSpecification(badBuyerId);
Assert.False(GetTestBasketCollection() var result = _evaluator.GetQuery(GetTestBasketCollection().AsQueryable(), spec)
.AsQueryable() .Any();
.Any(spec.Criterias.FirstOrDefault()));
Assert.False(result);
} }
public List<Basket> GetTestBasketCollection() public List<Basket> GetTestBasketCollection()

4
tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs

@ -14,7 +14,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()); .Where(spec.WhereExpressions.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(4, result.ToList().Count); Assert.Equal(4, result.ToList().Count);
@ -27,7 +27,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()); .Where(spec.WhereExpressions.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(2, result.ToList().Count); Assert.Equal(2, result.ToList().Count);

4
tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
namespace Microsoft.eShopWeb.UnitTests namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
{ {
public class CatalogFilterSpecification public class CatalogFilterSpecification
{ {
@ -21,7 +21,7 @@ namespace Microsoft.eShopWeb.UnitTests
var result = GetTestItemCollection() var result = GetTestItemCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()); .Where(spec.WhereExpressions.FirstOrDefault());
Assert.Equal(expectedCount, result.Count()); Assert.Equal(expectedCount, result.Count());
} }

4
tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs

@ -16,7 +16,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()); .Where(spec.WhereExpressions.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Single(result.ToList()); Assert.Single(result.ToList());
@ -30,7 +30,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()); .Where(spec.WhereExpressions.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal(2, result.ToList().Count); Assert.Equal(2, result.ToList().Count);

4
tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs

@ -17,7 +17,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.FirstOrDefault(spec.Criterias.FirstOrDefault()); .FirstOrDefault(spec.WhereExpressions.FirstOrDefault());
Assert.NotNull(result); Assert.NotNull(result);
Assert.NotNull(result.OrderItems); Assert.NotNull(result.OrderItems);
@ -32,7 +32,7 @@ namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications
var result = GetTestCollection() var result = GetTestCollection()
.AsQueryable() .AsQueryable()
.Where(spec.Criterias.FirstOrDefault()) .Where(spec.WhereExpressions.FirstOrDefault())
.ToList(); .ToList();
Assert.NotNull(result); Assert.NotNull(result);

Loading…
Cancel
Save