Skip to content

Commit

Permalink
Refactoring and Adding Tests (dotnet-architecture#28)
Browse files Browse the repository at this point in the history
* Introducing repository and refactoring services.
Changing entities to use int keys everywhere.

* Refactoring application services to live in web project and only reference repositories, not EF contexts.

* Cleaning up implementations

* Moving logic out of CatalogController
Moving entity knowledge out of viewmodels.

* Implementing specification includes better for catalogservice

* Cleaning up and adding specification unit tests
  • Loading branch information
ardalis authored Aug 7, 2017
1 parent 084db74 commit d7eb59c
Show file tree
Hide file tree
Showing 41 changed files with 449 additions and 360 deletions.
4 changes: 2 additions & 2 deletions src/ApplicationCore/Entities/BaseEntity.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class BaseEntity<T>
public class BaseEntity
{
public T Id { get; set; }
public int Id { get; set; }
}
}
11 changes: 5 additions & 6 deletions src/ApplicationCore/Entities/Basket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@

namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class Basket : BaseEntity<string>
public class Basket : BaseEntity
{
public string BuyerId { get; set; }
public List<BasketItem> Items { get; set; } = new List<BasketItem>();

public void AddItem(CatalogItem item, decimal unitPrice, int quantity = 1)
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
{
if(!Items.Any(i => i.Item.Id == item.Id))
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
{
Items.Add(new BasketItem()
{
Item = item,
//ProductId = productId,
CatalogItemId = catalogItemId,
Quantity = quantity,
UnitPrice = unitPrice
});
return;
}
var existingItem = Items.FirstOrDefault(i => i.Item.Id == item.Id);
var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
existingItem.Quantity += quantity;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/ApplicationCore/Entities/BasketItem.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class BasketItem : BaseEntity<string>
public class BasketItem : BaseEntity
{
//public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public CatalogItem Item { get; set; }
public int CatalogItemId { get; set; }
// public CatalogItem Item { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/ApplicationCore/Entities/CatalogBrand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogBrand : BaseEntity<int>
public class CatalogBrand : BaseEntity
{
public string Brand { get; set; }
}
Expand Down
2 changes: 1 addition & 1 deletion src/ApplicationCore/Entities/CatalogItem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogItem : BaseEntity<int>
public class CatalogItem : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/ApplicationCore/Entities/CatalogType.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogType : BaseEntity<int>
public class CatalogType : BaseEntity
{
public string Type { get; set; }
}
Expand Down
11 changes: 11 additions & 0 deletions src/ApplicationCore/Exceptions/CatalogImageMissingException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace ApplicationCore.Exceptions
{
/// <summary>
/// Note: No longer required.
/// </summary>
public class CatalogImageMissingException : Exception
{
public CatalogImageMissingException(string message,
Expand All @@ -14,5 +17,13 @@ public CatalogImageMissingException(Exception innerException)
innerException: innerException)
{
}

public CatalogImageMissingException() : base()
{
}

public CatalogImageMissingException(string message) : base(message)
{
}
}
}
4 changes: 4 additions & 0 deletions src/ApplicationCore/Interfaces/IAppLogger.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace ApplicationCore.Interfaces
{
/// <summary>
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IAppLogger<T>
{
void LogWarning(string message, params object[] args);
Expand Down
15 changes: 0 additions & 15 deletions src/ApplicationCore/Interfaces/IBasketService.cs

This file was deleted.

18 changes: 18 additions & 0 deletions src/ApplicationCore/Interfaces/IRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ApplicationCore.Interfaces
{

public interface IRepository<T> where T : BaseEntity
{
T GetById(int id);
List<T> List();
List<T> List(ISpecification<T> spec);
T Add(T entity);
void Update(T entity);
void Delete(T entity);
}
}
13 changes: 13 additions & 0 deletions src/ApplicationCore/Interfaces/ISpecification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace ApplicationCore.Interfaces
{
public interface ISpecification<T>
{
Expression<Func<T, bool>> Criteria { get; }
List<Expression<Func<T, object>>> Includes { get; }
void AddInclude(Expression<Func<T, object>> includeExpression);
}
}
5 changes: 4 additions & 1 deletion src/ApplicationCore/Interfaces/IUriComposer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace ApplicationCore.Interfaces
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Collections.Generic;

namespace ApplicationCore.Interfaces
{

public interface IUriComposer
Expand Down
6 changes: 2 additions & 4 deletions src/ApplicationCore/Services/UriComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ public class UriComposer : IUriComposer
{
private readonly CatalogSettings _catalogSettings;

public UriComposer(CatalogSettings catalogSettings)
{
_catalogSettings = catalogSettings;
}
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;

public string ComposePicUri(string uriTemplate)
{
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
Expand Down
28 changes: 28 additions & 0 deletions src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace ApplicationCore.Specifications
{
public class BasketWithItemsSpecification : ISpecification<Basket>
{
public BasketWithItemsSpecification(int basketId)
{
BasketId = basketId;
AddInclude(b => b.Items);
}

public int BasketId { get; }

public Expression<Func<Basket, bool>> Criteria => b => b.Id == BasketId;

public List<Expression<Func<Basket, object>>> Includes { get; } = new List<Expression<Func<Basket, object>>>();

public void AddInclude(Expression<Func<Basket, object>> includeExpression)
{
Includes.Add(includeExpression);
}
}
}
32 changes: 32 additions & 0 deletions src/ApplicationCore/Specifications/CatalogFilterSpecification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace ApplicationCore.Specifications
{

public class CatalogFilterSpecification : ISpecification<CatalogItem>
{
public CatalogFilterSpecification(int? brandId, int? typeId)
{
BrandId = brandId;
TypeId = typeId;
}

public int? BrandId { get; }
public int? TypeId { get; }

public Expression<Func<CatalogItem, bool>> Criteria =>
i => (!BrandId.HasValue || i.CatalogBrandId == BrandId) &&
(!TypeId.HasValue || i.CatalogTypeId == TypeId);

public List<Expression<Func<CatalogItem, object>>> Includes { get; } = new List<Expression<Func<CatalogItem, object>>>();

public void AddInclude(Expression<Func<CatalogItem, object>> includeExpression)
{
Includes.Add(includeExpression);
}
}
}
59 changes: 59 additions & 0 deletions src/Infrastructure/Data/EfRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using ApplicationCore.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Collections.Generic;
using System.Linq;

namespace Infrastructure.Data
{
public class EfRepository<T> : IRepository<T> where T : BaseEntity
{
private readonly CatalogContext _dbContext;

public EfRepository(CatalogContext dbContext)
{
_dbContext = dbContext;
}

public T GetById(int id)
{
return _dbContext.Set<T>().SingleOrDefault(e => e.Id == id);
}

public List<T> List()
{
return _dbContext.Set<T>().ToList();
}

public List<T> List(ISpecification<T> spec)
{
var queryableResultWithIncludes = spec.Includes
.Aggregate(_dbContext.Set<T>().AsQueryable(),
(current, include) => current.Include(include));
return queryableResultWithIncludes
.Where(spec.Criteria)
.ToList();
}

public T Add(T entity)
{
_dbContext.Set<T>().Add(entity);
_dbContext.SaveChanges();

return entity;
}

public void Delete(T entity)
{
_dbContext.Set<T>().Remove(entity);
_dbContext.SaveChanges();
}

public void Update(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
}

}
}
3 changes: 3 additions & 0 deletions src/Infrastructure/Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>

</Project>
63 changes: 0 additions & 63 deletions src/Infrastructure/Services/BasketService.cs

This file was deleted.

Loading

0 comments on commit d7eb59c

Please sign in to comment.