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:
9
src/ApplicationCore/Interfaces/IBasketQueryService.cs
Normal file
9
src/ApplicationCore/Interfaces/IBasketQueryService.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||||
|
{
|
||||||
|
public interface IBasketQueryService
|
||||||
|
{
|
||||||
|
Task<int> CountTotalBasketItems(string username);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
27
src/Infrastructure/Data/BasketQueryService.cs
Normal file
27
src/Infrastructure/Data/BasketQueryService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,94 +1,12 @@
|
|||||||
using Ardalis.Specification;
|
using Ardalis.Specification.EntityFrameworkCore;
|
||||||
using Ardalis.Specification.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Infrastructure.Data
|
namespace Microsoft.eShopWeb.Infrastructure.Data
|
||||||
{
|
{
|
||||||
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
|
public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
|
||||||
{
|
|
||||||
public EfRepository(CatalogContext dbContext) : base(dbContext)
|
|
||||||
{
|
{
|
||||||
|
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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@@ -13,13 +13,12 @@ namespace Microsoft.eShopWeb.Web.Configuration
|
|||||||
public static IServiceCollection AddCoreServices(this IServiceCollection services,
|
public static IServiceCollection AddCoreServices(this IServiceCollection services,
|
||||||
IConfiguration configuration)
|
IConfiguration configuration)
|
||||||
{
|
{
|
||||||
//services.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>));
|
|
||||||
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
|
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
|
||||||
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
|
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
|
||||||
|
|
||||||
services.AddScoped<IBasketService, BasketService>();
|
services.AddScoped<IBasketService, BasketService>();
|
||||||
services.AddScoped<IOrderService, OrderService>();
|
services.AddScoped<IOrderService, OrderService>();
|
||||||
//services.AddScoped<IOrderRepository, OrderRepository>();
|
services.AddScoped<IBasketQueryService, BasketQueryService>();
|
||||||
services.AddSingleton<IUriComposer>(new UriComposer(configuration.Get<CatalogSettings>()));
|
services.AddSingleton<IUriComposer>(new UriComposer(configuration.Get<CatalogSettings>()));
|
||||||
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
||||||
services.AddTransient<IEmailSender, EmailSender>();
|
services.AddTransient<IEmailSender, EmailSender>();
|
||||||
|
|||||||
@@ -14,14 +14,17 @@ namespace Microsoft.eShopWeb.Web.Services
|
|||||||
{
|
{
|
||||||
private readonly IRepository<Basket> _basketRepository;
|
private readonly IRepository<Basket> _basketRepository;
|
||||||
private readonly IUriComposer _uriComposer;
|
private readonly IUriComposer _uriComposer;
|
||||||
|
private readonly IBasketQueryService _basketQueryService;
|
||||||
private readonly IRepository<CatalogItem> _itemRepository;
|
private readonly IRepository<CatalogItem> _itemRepository;
|
||||||
|
|
||||||
public BasketViewModelService(IRepository<Basket> basketRepository,
|
public BasketViewModelService(IRepository<Basket> basketRepository,
|
||||||
IRepository<CatalogItem> itemRepository,
|
IRepository<CatalogItem> itemRepository,
|
||||||
IUriComposer uriComposer)
|
IUriComposer uriComposer,
|
||||||
|
IBasketQueryService basketQueryService)
|
||||||
{
|
{
|
||||||
_basketRepository = basketRepository;
|
_basketRepository = basketRepository;
|
||||||
_uriComposer = uriComposer;
|
_uriComposer = uriComposer;
|
||||||
|
_basketQueryService = basketQueryService;
|
||||||
_itemRepository = itemRepository;
|
_itemRepository = itemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +39,7 @@ namespace Microsoft.eShopWeb.Web.Services
|
|||||||
}
|
}
|
||||||
var viewModel = await Map(basket);
|
var viewModel = await Map(basket);
|
||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<BasketViewModel> CreateBasketForUser(string userId)
|
private async Task<BasketViewModel> CreateBasketForUser(string userId)
|
||||||
{
|
{
|
||||||
@@ -79,18 +82,16 @@ namespace Microsoft.eShopWeb.Web.Services
|
|||||||
return new BasketViewModel()
|
return new BasketViewModel()
|
||||||
{
|
{
|
||||||
BuyerId = basket.BuyerId,
|
BuyerId = basket.BuyerId,
|
||||||
Id = basket.Id,
|
Id = basket.Id,
|
||||||
Items = await GetBasketItems(basket.Items)
|
Items = await GetBasketItems(basket.Items)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> CountTotalBasketItems(string username)
|
public async Task<int> CountTotalBasketItems(string username)
|
||||||
{
|
{
|
||||||
var basketSpec = new BasketWithItemsSpecification(username);
|
var counter = await _basketQueryService.CountTotalBasketItems(username);
|
||||||
var basket = await _basketRepository.GetBySpecAsync(basketSpec);
|
|
||||||
if (basket == null)
|
return counter;
|
||||||
return 0;
|
|
||||||
return basket.Items.Sum(i => i.Quantity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user