Skip to content

Commit

Permalink
Use cancellation token in repository and web fie system (http call ca…
Browse files Browse the repository at this point in the history
…n cancelled) (dotnet-architecture#471)

Co-authored-by: cmichel <cmichel@interparking.com>
  • Loading branch information
michelcedric and cmichel authored Nov 30, 2020
1 parent 2502e01 commit 6041a1f
Show file tree
Hide file tree
Showing 19 changed files with 95 additions and 97 deletions.
17 changes: 7 additions & 10 deletions src/ApplicationCore/Entities/CatalogItem.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System;
using Ardalis.GuardClauses;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Collections.Generic;
using System;

namespace Microsoft.eShopWeb.ApplicationCore.Entities
{


public class CatalogItem : BaseEntity, IAggregateRoot
{
public string Name { get; private set; }
Expand All @@ -18,11 +15,11 @@ public class CatalogItem : BaseEntity, IAggregateRoot
public int CatalogBrandId { get; private set; }
public CatalogBrand CatalogBrand { get; private set; }

public CatalogItem(int catalogTypeId,
int catalogBrandId,
string description,
string name,
decimal price,
public CatalogItem(int catalogTypeId,
int catalogBrandId,
string description,
string name,
decimal price,
string pictureUri)
{
CatalogTypeId = catalogTypeId;
Expand Down
19 changes: 10 additions & 9 deletions src/ApplicationCore/Interfaces/IAsyncRepository.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IAsyncRepository<T> where T : BaseEntity, IAggregateRoot
{
Task<T> GetByIdAsync(int id);
Task<IReadOnlyList<T>> ListAllAsync();
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
Task<int> CountAsync(ISpecification<T> spec);
Task<T> FirstAsync(ISpecification<T> spec);
Task<T> FirstOrDefaultAsync(ISpecification<T> spec);
Task<T> GetByIdAsync(int id, CancellationToken cancellationToken = default);
Task<IReadOnlyList<T>> ListAllAsync(CancellationToken cancellationToken = default);
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec, CancellationToken cancellationToken = default);
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
Task<int> CountAsync(ISpecification<T> spec, CancellationToken cancellationToken = default);
Task<T> FirstAsync(ISpecification<T> spec, CancellationToken cancellationToken = default);
Task<T> FirstOrDefaultAsync(ISpecification<T> spec, CancellationToken cancellationToken = default);
}
}
5 changes: 3 additions & 2 deletions src/ApplicationCore/Interfaces/IFileSystem.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IFileSystem
{
Task<bool> SavePicture(string pictureName, string pictureBase64);
Task<bool> SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken);
}
}
38 changes: 20 additions & 18 deletions src/Infrastructure/Data/EfRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Infrastructure.Data
Expand All @@ -23,58 +24,59 @@ public EfRepository(CatalogContext dbContext)
_dbContext = dbContext;
}

public virtual async Task<T> GetByIdAsync(int id)
public virtual async Task<T> GetByIdAsync(int id, CancellationToken cancellationToken = default)
{
return await _dbContext.Set<T>().FindAsync(id);
var keyValues = new object[] { id };
return await _dbContext.Set<T>().FindAsync(keyValues, cancellationToken);
}

public async Task<IReadOnlyList<T>> ListAllAsync()
public async Task<IReadOnlyList<T>> ListAllAsync(CancellationToken cancellationToken = default)
{
return await _dbContext.Set<T>().ToListAsync();
return await _dbContext.Set<T>().ToListAsync(cancellationToken);
}

public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec)
public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
{
var specificationResult = ApplySpecification(spec);
return await specificationResult.ToListAsync();
return await specificationResult.ToListAsync(cancellationToken);
}

public async Task<int> CountAsync(ISpecification<T> spec)
public async Task<int> CountAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
{
var specificationResult = ApplySpecification(spec);
return await specificationResult.CountAsync();
return await specificationResult.CountAsync(cancellationToken);
}

public async Task<T> AddAsync(T entity)
public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
{
await _dbContext.Set<T>().AddAsync(entity);
await _dbContext.SaveChangesAsync();
await _dbContext.SaveChangesAsync(cancellationToken);

return entity;
}

public async Task UpdateAsync(T entity)
public async Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
await _dbContext.SaveChangesAsync(cancellationToken);
}

public async Task DeleteAsync(T entity)
public async Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
{
_dbContext.Set<T>().Remove(entity);
await _dbContext.SaveChangesAsync();
await _dbContext.SaveChangesAsync(cancellationToken);
}

public async Task<T> FirstAsync(ISpecification<T> spec)
public async Task<T> FirstAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
{
var specificationResult = ApplySpecification(spec);
return await specificationResult.FirstAsync();
return await specificationResult.FirstAsync(cancellationToken);
}

public async Task<T> FirstOrDefaultAsync(ISpecification<T> spec)
public async Task<T> FirstOrDefaultAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
{
var specificationResult = ApplySpecification(spec);
return await specificationResult.FirstOrDefaultAsync();
return await specificationResult.FirstOrDefaultAsync(cancellationToken);
}

private IQueryable<T> ApplySpecification(ISpecification<T> spec)
Expand Down
23 changes: 12 additions & 11 deletions src/Infrastructure/Services/WebFileSystem.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using System;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;

namespace Microsoft.eShopWeb.Infrastructure.Services
{
public class WebFileSystem: IFileSystem
public class WebFileSystem : IFileSystem
{
private readonly HttpClient _httpClient;
private readonly string _url;
Expand All @@ -22,36 +23,36 @@ public WebFileSystem(string url)
_httpClient.DefaultRequestHeaders.Add("auth-key", AUTH_KEY);
}

public async Task<bool> SavePicture(string pictureName, string pictureBase64)
public async Task<bool> SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(pictureBase64) || !await UploadFile(pictureName, Convert.FromBase64String(pictureBase64)))
if (string.IsNullOrEmpty(pictureBase64) || !await UploadFile(pictureName, Convert.FromBase64String(pictureBase64), cancellationToken))
{
return false;
}

return true;
}

private async Task<bool> UploadFile(string fileName, byte[] fileData)
private async Task<bool> UploadFile(string fileName, byte[] fileData, CancellationToken cancellationToken)
{
if (!fileData.IsValidImage(fileName))
{
return false;
}

return await UploadToWeb(fileName, fileData);
return await UploadToWeb(fileName, fileData, cancellationToken);
}

private async Task<bool> UploadToWeb(string fileName, byte[] fileData)
private async Task<bool> UploadToWeb(string fileName, byte[] fileData, CancellationToken cancellationToken)
{
var request = new FileItem
{
DataBase64 = Convert.ToBase64String(fileData),
DataBase64 = Convert.ToBase64String(fileData),
FileName = fileName
};
var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");

using var message = await _httpClient.PostAsync(_url, content);
using var message = await _httpClient.PostAsync(_url, content, cancellationToken);
if (!message.IsSuccessStatusCode)
{
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/PublicApi/CatalogBrandEndpoints/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public override async Task<ActionResult<ListCatalogBrandsResponse>> HandleAsync(
{
var response = new ListCatalogBrandsResponse();

var items = await _catalogBrandRepository.ListAllAsync();
var items = await _catalogBrandRepository.ListAllAsync(cancellationToken);

response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));

Expand Down
14 changes: 7 additions & 7 deletions src/PublicApi/CatalogItemEndpoints/Create.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System.IO;
using System.Threading;
using Ardalis.ApiEndpoints;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Swashbuckle.AspNetCore.Annotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
Expand Down Expand Up @@ -39,15 +39,15 @@ public override async Task<ActionResult<CreateCatalogItemResponse>> HandleAsync(

var newItem = new CatalogItem(request.CatalogTypeId, request.CatalogBrandId, request.Description, request.Name, request.Price, request.PictureUri);

newItem = await _itemRepository.AddAsync(newItem);
newItem = await _itemRepository.AddAsync(newItem, cancellationToken);

if (newItem.Id != 0)
{
var picName = $"{newItem.Id}{Path.GetExtension(request.PictureName)}";
if (await _webFileSystem.SavePicture(picName, request.PictureBase64))
if (await _webFileSystem.SavePicture(picName, request.PictureBase64, cancellationToken))
{
newItem.UpdatePictureUri(picName);
await _itemRepository.UpdateAsync(newItem);
await _itemRepository.UpdateAsync(newItem, cancellationToken);
}
}

Expand All @@ -65,6 +65,6 @@ public override async Task<ActionResult<CreateCatalogItemResponse>> HandleAsync(
return response;
}


}
}
11 changes: 5 additions & 6 deletions src/PublicApi/CatalogItemEndpoints/Delete.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Threading;
using Ardalis.ApiEndpoints;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Constants;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
Expand All @@ -28,14 +27,14 @@ public Delete(IAsyncRepository<CatalogItem> itemRepository)
OperationId = "catalog-items.Delete",
Tags = new[] { "CatalogItemEndpoints" })
]
public override async Task<ActionResult<DeleteCatalogItemResponse>> HandleAsync([FromRoute]DeleteCatalogItemRequest request, CancellationToken cancellationToken)
public override async Task<ActionResult<DeleteCatalogItemResponse>> HandleAsync([FromRoute] DeleteCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new DeleteCatalogItemResponse(request.CorrelationId());

var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId);
var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken);
if (itemToDelete is null) return NotFound();

await _itemRepository.DeleteAsync(itemToDelete);
await _itemRepository.DeleteAsync(itemToDelete, cancellationToken);

return Ok(response);
}
Expand Down
6 changes: 3 additions & 3 deletions src/PublicApi/CatalogItemEndpoints/GetById.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Threading;
using Ardalis.ApiEndpoints;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
Expand All @@ -30,7 +30,7 @@ public override async Task<ActionResult<GetByIdCatalogItemResponse>> HandleAsync
{
var response = new GetByIdCatalogItemResponse(request.CorrelationId());

var item = await _itemRepository.GetByIdAsync(request.CatalogItemId);
var item = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken);
if (item is null) return NotFound();

response.CatalogItem = new CatalogItemDto
Expand Down
6 changes: 3 additions & 3 deletions src/PublicApi/CatalogItemEndpoints/ListPaged.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ public ListPaged(IAsyncRepository<CatalogItem> itemRepository,
OperationId = "catalog-items.ListPaged",
Tags = new[] { "CatalogItemEndpoints" })
]
public override async Task<ActionResult<ListPagedCatalogItemResponse>> HandleAsync([FromQuery]ListPagedCatalogItemRequest request, CancellationToken cancellationToken)
public override async Task<ActionResult<ListPagedCatalogItemResponse>> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new ListPagedCatalogItemResponse(request.CorrelationId());

var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId);
int totalItems = await _itemRepository.CountAsync(filterSpec);
int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken);

var pagedSpec = new CatalogFilterPaginatedSpecification(
skip: request.PageIndex * request.PageSize,
take: request.PageSize,
brandId: request.CatalogBrandId,
typeId: request.CatalogTypeId);

var items = await _itemRepository.ListAsync(pagedSpec);
var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken);

response.CatalogItems.AddRange(items.Select(_mapper.Map<CatalogItemDto>));
foreach (CatalogItemDto item in response.CatalogItems)
Expand Down
6 changes: 3 additions & 3 deletions src/PublicApi/CatalogItemEndpoints/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public override async Task<ActionResult<UpdateCatalogItemResponse>> HandleAsync(
{
var response = new UpdateCatalogItemResponse(request.CorrelationId());

var existingItem = await _itemRepository.GetByIdAsync(request.Id);
var existingItem = await _itemRepository.GetByIdAsync(request.Id, cancellationToken);

existingItem.UpdateDetails(request.Name, request.Description, request.Price);
existingItem.UpdateBrand(request.CatalogBrandId);
Expand All @@ -50,13 +50,13 @@ public override async Task<ActionResult<UpdateCatalogItemResponse>> HandleAsync(
else
{
var picName = $"{existingItem.Id}{Path.GetExtension(request.PictureName)}";
if (await _webFileSystem.SavePicture($"{picName}", request.PictureBase64))
if (await _webFileSystem.SavePicture($"{picName}", request.PictureBase64, cancellationToken))
{
existingItem.UpdatePictureUri(picName);
}
}

await _itemRepository.UpdateAsync(existingItem);
await _itemRepository.UpdateAsync(existingItem, cancellationToken);

var dto = new CatalogItemDto
{
Expand Down
2 changes: 1 addition & 1 deletion src/PublicApi/CatalogTypeEndpoints/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public override async Task<ActionResult<ListCatalogTypesResponse>> HandleAsync(C
{
var response = new ListCatalogTypesResponse();

var items = await _catalogTypeRepository.ListAllAsync();
var items = await _catalogTypeRepository.ListAllAsync(cancellationToken);

response.CatalogTypes.AddRange(items.Select(_mapper.Map<CatalogTypeDto>));

Expand Down
2 changes: 1 addition & 1 deletion src/Web/Features/MyOrders/GetMyOrdersHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public GetMyOrdersHandler(IOrderRepository orderRepository)
public async Task<IEnumerable<OrderViewModel>> Handle(GetMyOrders request, CancellationToken cancellationToken)
{
var specification = new CustomerOrdersWithItemsSpecification(request.UserName);
var orders = await _orderRepository.ListAsync(specification);
var orders = await _orderRepository.ListAsync(specification, cancellationToken);

return orders.Select(o => new OrderViewModel
{
Expand Down
Loading

0 comments on commit 6041a1f

Please sign in to comment.