replace counter by specific query to avoid load each items in memory (#611)

* replace counter by specific query to avoid load each items in memory

* Create IBasketQueryService
This commit is contained in:
Cédric Michel
2021-11-02 15:16:57 +01:00
committed by GitHub
parent 553a10187d
commit 64f150dc07
7 changed files with 50 additions and 129 deletions

View File

@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IBasketQueryService
{
Task<int> CountTotalBasketItems(string username);
}
}

View File

@@ -1,11 +0,0 @@
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
//public interface IOrderRepository : IAsyncRepository<Order>
//{
// Task<Order> GetByIdWithItemsAsync(int id);
//}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Infrastructure.Data
{
public class BasketQueryService : IBasketQueryService
{
private readonly CatalogContext _dbContext;
public BasketQueryService(CatalogContext dbContext)
{
_dbContext = dbContext;
}
public async Task<int> CountTotalBasketItems(string username)
{
var totalItems = await _dbContext.Baskets
.Where(basket => basket.BuyerId == username)
.SelectMany(item => item.Items)
.SumAsync(sum => sum.Quantity);
return totalItems;
}
}
}

View File

@@ -1,94 +1,12 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Ardalis.Specification.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Infrastructure.Data
{
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
{
public EfRepository(CatalogContext dbContext) : base(dbContext)
{
}
}
/// <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> : IAsyncRepository<T> where T : BaseEntity, IAggregateRoot
// {
// protected readonly CatalogContext _dbContext;
// public EfRepository(CatalogContext dbContext)
// {
// _dbContext = dbContext;
// }
// public virtual async Task<T> GetByIdAsync(int id, CancellationToken cancellationToken = default)
// {
// var keyValues = new object[] { id };
// return await _dbContext.Set<T>().FindAsync(keyValues, cancellationToken);
// }
// public async Task<IReadOnlyList<T>> ListAllAsync(CancellationToken cancellationToken = default)
// {
// return await _dbContext.Set<T>().ToListAsync(cancellationToken);
// }
// public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
// {
// var specificationResult = ApplySpecification(spec);
// return await specificationResult.ToListAsync(cancellationToken);
// }
// public async Task<int> CountAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
// {
// var specificationResult = ApplySpecification(spec);
// return await specificationResult.CountAsync(cancellationToken);
// }
// public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
// {
// await _dbContext.Set<T>().AddAsync(entity, cancellationToken);
// await _dbContext.SaveChangesAsync(cancellationToken);
// return entity;
// }
// public async Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
// {
// _dbContext.Entry(entity).State = EntityState.Modified;
// await _dbContext.SaveChangesAsync(cancellationToken);
// }
// public async Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
// {
// _dbContext.Set<T>().Remove(entity);
// await _dbContext.SaveChangesAsync(cancellationToken);
// }
// public async Task<T> FirstAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
// {
// var specificationResult = ApplySpecification(spec);
// return await specificationResult.FirstAsync(cancellationToken);
// }
// public async Task<T> FirstOrDefaultAsync(ISpecification<T> spec, CancellationToken cancellationToken = default)
// {
// var specificationResult = ApplySpecification(spec);
// return await specificationResult.FirstOrDefaultAsync(cancellationToken);
// }
// private IQueryable<T> ApplySpecification(ISpecification<T> spec)
// {
// var evaluator = new SpecificationEvaluator<T>();
// return evaluator.GetQuery(_dbContext.Set<T>().AsQueryable(), spec);
// }
// }
}
}

View File

@@ -1,22 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Infrastructure.Data
{
//public class OrderRepository : EfRepository<Order>, IOrderRepository
//{
// public OrderRepository(CatalogContext dbContext) : base(dbContext)
// {
// }
// public Task<Order> GetByIdWithItemsAsync(int id)
// {
// return _dbContext.Orders
// .Include(o => o.OrderItems)
// .ThenInclude(i => i.ItemOrdered)
// .FirstOrDefaultAsync(x => x.Id == id);
// }
//}
}

View File

@@ -13,13 +13,12 @@ namespace Microsoft.eShopWeb.Web.Configuration
public static IServiceCollection AddCoreServices(this IServiceCollection services,
IConfiguration configuration)
{
//services.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped<IBasketService, BasketService>();
services.AddScoped<IOrderService, OrderService>();
//services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IBasketQueryService, BasketQueryService>();
services.AddSingleton<IUriComposer>(new UriComposer(configuration.Get<CatalogSettings>()));
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
services.AddTransient<IEmailSender, EmailSender>();

View File

@@ -14,14 +14,17 @@ namespace Microsoft.eShopWeb.Web.Services
{
private readonly IRepository<Basket> _basketRepository;
private readonly IUriComposer _uriComposer;
private readonly IBasketQueryService _basketQueryService;
private readonly IRepository<CatalogItem> _itemRepository;
public BasketViewModelService(IRepository<Basket> basketRepository,
IRepository<CatalogItem> itemRepository,
IUriComposer uriComposer)
IUriComposer uriComposer,
IBasketQueryService basketQueryService)
{
_basketRepository = basketRepository;
_uriComposer = uriComposer;
_basketQueryService = basketQueryService;
_itemRepository = itemRepository;
}
@@ -86,11 +89,9 @@ namespace Microsoft.eShopWeb.Web.Services
public async Task<int> CountTotalBasketItems(string username)
{
var basketSpec = new BasketWithItemsSpecification(username);
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
if (basket == null)
return 0;
return basket.Items.Sum(i => i.Quantity);
var counter = await _basketQueryService.CountTotalBasketItems(username);
return counter;
}
}
}