Permalink
Browse files

Completely removed cache options from RemoteQueries so queries now ar…

…e cache dependency free. Realized CachingCommand & refactored RemoteQueryCacheDecorator to decorate only commands which comes as CachingCommand type.
  • Loading branch information...
ErikasKontenis committed May 17, 2018
1 parent d181ded commit bf935ee277d302b761b012444da75b6a09a0357a
Showing with 79 additions and 76 deletions.
  1. +4 −1 Ek.Shop.Application.Services/Categories/SaveCategoryQueryHandler.cs
  2. +4 −1 Ek.Shop.Application.Services/Products/SaveProductQueryHandler.cs
  3. +0 −7 Ek.Shop.Base.Data/Queries/RemoteQueries/IRemoteQuery.cs
  4. +1 −16 Ek.Shop.Base.Data/Queries/RemoteQueries/RemoteQuery.cs
  5. +5 −12 Ek.Shop.Base.Data/Queries/RemoteQueries/RemoteQueryCacheDecorator.cs
  6. +20 −0 Ek.Shop.Contracts/Abstractions/CachingCommand.cs
  7. +7 −2 Ek.Shop.Contracts/Commands/GetCategoryCommand.cs
  8. +7 −2 Ek.Shop.Contracts/Commands/GetProductCommand.cs
  9. +27 −0 Ek.Shop.Core/Enums/CacheExpirationModes.cs
  10. +0 −2 Ek.Shop.Data/Baskets/AddProductToBasketQuery.cs
  11. +0 −2 Ek.Shop.Data/Baskets/CreateOrGetUnconfirmedBasketQuery.cs
  12. +0 −2 Ek.Shop.Data/Baskets/DeleteBasketItemQuery.cs
  13. +0 −2 Ek.Shop.Data/Categories/DeleteCategoryQuery.cs
  14. +0 −2 Ek.Shop.Data/Categories/GetCategoryQuery.cs
  15. +0 −2 Ek.Shop.Data/Categories/ListCategoriesQuery.cs
  16. +0 −2 Ek.Shop.Data/Categories/SaveCategoryQuery.cs
  17. +0 −2 Ek.Shop.Data/Orders/GetOrderQuery.cs
  18. +0 −2 Ek.Shop.Data/Orders/ListOrdersQuery.cs
  19. +0 −2 Ek.Shop.Data/Products/DeleteProductQuery.cs
  20. +0 −3 Ek.Shop.Data/Products/GetProductQuery.cs
  21. +0 −2 Ek.Shop.Data/Products/ListProductsByCategoryQuery.cs
  22. +0 −2 Ek.Shop.Data/Products/ListProductsQuery.cs
  23. +0 −2 Ek.Shop.Data/Products/SaveProductQuery.cs
  24. +0 −2 Ek.Shop.Data/Routes/GetRouteQuery.cs
  25. +0 −2 Ek.Shop.Data/Routes/ListRoutesQuery.cs
  26. +4 −2 Ek.Shop.Web/Startup.cs
@@ -68,7 +68,10 @@ public override async Task<ActionResult<CategoryDto>> Handle(SaveCategoryCommand
var classifierStore = await _classifierStoresRepository.Get();
var availableCharacteristics = await _listCharacteristicsQuery.Query(new ListCharacteristicsCommand());
var category = await _getCategoryQuery.Query(new GetCategoryCommand(command.SaveCategory.Category.Id, command.LanguageId, true)) ?? new Category();
var category = await _getCategoryQuery.Query(new GetCategoryCommand(command.SaveCategory.Category.Id, command.LanguageId, true)
{
IsFromCache = false
}) ?? new Category();
var categoryDto = command.SaveCategory.Category;
categoryDto = await CategoryDtoDefaults(categoryDto);
@@ -69,7 +69,10 @@ public override async Task<ActionResult<ProductDto>> Handle(SaveProductCommand c
var classifierStore = await _classifierStoresRepository.Get();
var availableCharacteristics = await _listCharacteristicsQuery.Query(new ListCharacteristicsCommand());
var product = await _getProductQuery.Query(new GetProductCommand(command.SaveProduct.Product.Id, command.LanguageId)) ?? new Product();
var product = await _getProductQuery.Query(new GetProductCommand(command.SaveProduct.Product.Id, command.LanguageId)
{
IsFromCache = false
}) ?? new Product();
var productDto = command.SaveProduct.Product;
_mapper.Map(productDto, product);
@@ -1,17 +1,10 @@
using Ek.Shop.Contracts.Commands;
using Ek.Shop.Core.Models;
using System.Threading.Tasks;
namespace Ek.Shop.Base.Data.Queries.RemoteQueries
{
public interface IRemoteQuery<TCommand, TResult> where TResult : class where TCommand : ICommand
{
CacheOptions CacheOptions { get; }
string CacheRegion { get; }
bool IsCacheRequired { get; }
Task<TResult> Query(TCommand command);
}
}
@@ -1,9 +1,5 @@
using CacheManager.Core;
using Ek.Shop.Base.Data.DbContexts;
using Ek.Shop.Base.Data.DbContexts;
using Ek.Shop.Contracts.Commands;
using Ek.Shop.Core.Enums;
using Ek.Shop.Core.Models;
using System;
using System.Threading.Tasks;
namespace Ek.Shop.Base.Data.Queries.RemoteQueries
@@ -17,17 +13,6 @@ public RemoteQuery(EkShopContext dbContext)
DbContext = dbContext;
}
public virtual CacheOptions CacheOptions => new CacheOptions()
{
ExpirationMode = (int)ExpirationMode.Absolute,
Timeout = TimeSpan.FromMinutes(15)
};
public virtual string CacheRegion => CacheRegions.Default;
public virtual bool IsCacheRequired => true;
public abstract Task<TResult> Query(TCommand command);
}
}
@@ -1,13 +1,12 @@
using CacheManager.Core;
using Ek.Shop.Base.Data.Caches;
using Ek.Shop.Contracts.Commands;
using Ek.Shop.Contracts.Abstractions;
using Ek.Shop.Contracts.Extensions;
using Ek.Shop.Core.Models;
using System.Threading.Tasks;
namespace Ek.Shop.Base.Data.Queries.RemoteQueries
{
public class RemoteQueryCacheDecorator<TCommand, TResult> : IRemoteQuery<TCommand, TResult> where TResult : class where TCommand : ICommand
public class RemoteQueryCacheDecorator<TCommand, TResult> : IRemoteQuery<TCommand, TResult> where TResult : class where TCommand : CachingCommand
{
private readonly IRemoteQuery<TCommand, TResult> _decoratedRemoteQuery;
private readonly ICache _cache;
@@ -19,28 +18,22 @@ public class RemoteQueryCacheDecorator<TCommand, TResult> : IRemoteQuery<TComman
_cache = cache;
}
public CacheOptions CacheOptions => _decoratedRemoteQuery.CacheOptions;
public string CacheRegion => _decoratedRemoteQuery.CacheRegion;
public bool IsCacheRequired => _decoratedRemoteQuery.IsCacheRequired;
public async Task<TResult> Query(TCommand command)
{
if (!IsCacheRequired)
if (!command.IsFromCache)
{
return await _decoratedRemoteQuery.Query(command);
}
var commandCacheKey = typeof(TCommand).FullName + command.ToJson();
var cachedItem = _cache.Get<TResult>(commandCacheKey, CacheRegion);
var cachedItem = _cache.Get<TResult>(commandCacheKey, command.Region);
if (cachedItem == null)
{
var item = await _decoratedRemoteQuery.Query(command);
if (item == null)
return item;
var cacheItem = new CacheItem<object>(commandCacheKey, CacheRegion, item, (ExpirationMode)CacheOptions.ExpirationMode, CacheOptions.Timeout);
var cacheItem = new CacheItem<object>(commandCacheKey, command.Region, item, (ExpirationMode)command.ExpirationMode, command.Timeout);
_cache.Add(cacheItem);
@@ -0,0 +1,20 @@
using Ek.Shop.Contracts.Commands;
using Ek.Shop.Core.Enums;
using System;
namespace Ek.Shop.Contracts.Abstractions
{
public abstract class CachingCommand : ICommand
{
public CachingCommand()
{ }
public abstract string Region { get; }
public virtual int ExpirationMode { get { return CacheExpirationModes.Absolute; } }
public bool IsFromCache { get; set; } = true;
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(15);
}
}
@@ -1,6 +1,9 @@
namespace Ek.Shop.Contracts.Commands
using Ek.Shop.Contracts.Abstractions;
using Ek.Shop.Core.Enums;
namespace Ek.Shop.Contracts.Commands
{
public class GetCategoryCommand : ICommand
public class GetCategoryCommand : CachingCommand
{
public GetCategoryCommand()
{ }
@@ -17,5 +20,7 @@ public GetCategoryCommand(int categoryId, int languageId, bool isDisabledInclude
public bool IsDisabledIncluded { get; set; }
public int LanguageId { get; set; }
public override string Region => CacheRegions.Category;
}
}
@@ -1,6 +1,9 @@
namespace Ek.Shop.Contracts.Commands
using Ek.Shop.Contracts.Abstractions;
using Ek.Shop.Core.Enums;
namespace Ek.Shop.Contracts.Commands
{
public class GetProductCommand : ICommand
public class GetProductCommand : CachingCommand
{
public GetProductCommand()
{ }
@@ -14,5 +17,7 @@ public GetProductCommand(int productId, int languageId)
public int LanguageId { get; set; }
public int ProductId { get; set; }
public override string Region => CacheRegions.Product;
}
}
@@ -0,0 +1,27 @@
namespace Ek.Shop.Core.Enums
{
public static class CacheExpirationModes
{
/// <summary>
/// Default value for the expircation mode enum. CacheManager will default to None.
/// The Default entry in the enum is used as separation from the other values and
/// to make it possible to explicitly set the expiration to None.
/// </summary>
public static int Default = 0;
/// <summary>
/// Defines no expiration.
/// </summary>
public static int None = 1;
/// <summary>
/// Defines sliding expiration. The expiration timeout will be refreshed on every access.
/// </summary>
public static int Sliding = 2;
/// <summary>
/// Defines absolute expiration. The item will expire after the expiration timeout.
/// </summary>
public static int Absolute = 3;
}
}
@@ -27,8 +27,6 @@ public class AddProductToBasketQuery : RemoteQuery<AddProductToBasketCommand, Ba
_workContext = workContext;
}
public override bool IsCacheRequired => false;
public override async Task<Basket> Query(AddProductToBasketCommand command)
{
var product = await _getProductQuery.Query(new GetProductCommand(command.ProductId, _workContext.WorkingLanguageId));
@@ -26,8 +26,6 @@ public class CreateOrGetUnconfirmedBasketQuery : RemoteQuery<CreateOrGetUnconfir
_userManager = userManager;
}
public override bool IsCacheRequired => false;
public override async Task<Basket> Query(CreateOrGetUnconfirmedBasketCommand command)
{
var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
@@ -20,8 +20,6 @@ public class DeleteBasketItemQuery : RemoteQuery<DeleteBasketItemCommand, Basket
_createOrGetUnconfirmedBasketQuery = createOrGetUnconfirmedBasketQuery;
}
public override bool IsCacheRequired => false;
public override async Task<BasketItem> Query(DeleteBasketItemCommand command)
{
var basket = await _createOrGetUnconfirmedBasketQuery.Query(new CreateOrGetUnconfirmedBasketCommand());
@@ -27,8 +27,6 @@ public class DeleteCategoryQuery : RemoteQuery<DeleteCategoryCommand, Category>
_cache = cache;
}
public override bool IsCacheRequired => false;
public override async Task<Category> Query(DeleteCategoryCommand command)
{
var category = await DbContext.Categories.Include(o => o.Route).FirstOrDefaultAsync(o => o.Id == command.CategoryId);
@@ -26,8 +26,6 @@ public class GetCategoryQuery : RemoteQuery<GetCategoryCommand, Category>
_workContext = workContext;
}
public override string CacheRegion => CacheRegions.Category;
public override async Task<Category> Query(GetCategoryCommand command)
{
var categoriesQuery = _categoryBaseQuery.Execute(command);
@@ -17,8 +17,6 @@ public ListCategoriesQuery(EkShopContext dbContext)
: base(dbContext)
{ }
public override string CacheRegion => CacheRegions.Category;
public override async Task<PagedList<Category>> Query(ListCategoriesCommand command)
{
// ServerSide paging of category list is doing in query handler only because it is too difficult to properly form navigations
@@ -20,8 +20,6 @@ public class SaveCategoryQuery : RemoteQuery<SaveCategoryQueryCommand, Category>
_cache = cache;
}
public override bool IsCacheRequired => false;
public override async Task<Category> Query(SaveCategoryQueryCommand command)
{
DbContext.AddOrUpdate(command.Category);
@@ -14,8 +14,6 @@ public GetOrderQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override bool IsCacheRequired => false;
public override async Task<Order> Query(GetOrderCommand command)
{
var query = DbContext.Orders
@@ -16,8 +16,6 @@ public ListOrdersQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override bool IsCacheRequired => false;
public override async Task<PagedList<Order>> Query(ListOrdersCommand command)
{
var query = DbContext.Orders
@@ -21,8 +21,6 @@ public class DeleteProductQuery : RemoteQuery<DeleteProductCommand, Product>
_cache = cache;
}
public override bool IsCacheRequired => false;
public override async Task<Product> Query(DeleteProductCommand command)
{
var product = await DbContext.Products.Include(o => o.Route).FirstOrDefaultAsync(o => o.Id == command.ProductId);
@@ -3,7 +3,6 @@
using Ek.Shop.Base.Data.Queries.Queries;
using Ek.Shop.Base.Data.Queries.RemoteQueries;
using Ek.Shop.Contracts.Commands;
using Ek.Shop.Core.Enums;
using Ek.Shop.Domain.Categories;
using Ek.Shop.Domain.Products;
using Microsoft.EntityFrameworkCore;
@@ -25,8 +24,6 @@ public class GetProductQuery : RemoteQuery<GetProductCommand, Product>
_getCategoryBaseQuery = getCategoryBaseQuery;
}
public override string CacheRegion => CacheRegions.Product;
public override async Task<Product> Query(GetProductCommand command)
{
var product = await DbContext.Products
@@ -18,8 +18,6 @@ public ListProductsByCategoryQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override string CacheRegion => CacheRegions.Product;
public override async Task<PagedList<Product>> Query(ListProductsByCategoryCommand command)
{
var includableQueryable = DbContext.Products
@@ -17,8 +17,6 @@ public ListProductsQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override string CacheRegion => CacheRegions.Product;
public override async Task<PagedList<Product>> Query(ListProductsCommand command)
{
var query = DbContext.Products
@@ -20,8 +20,6 @@ public class SaveProductQuery : RemoteQuery<SaveProductQueryCommand, Product>
_cache = cache;
}
public override bool IsCacheRequired => false;
public override async Task<Product> Query(SaveProductQueryCommand command)
{
DbContext.AddOrUpdate(command.Product);
@@ -13,8 +13,6 @@ public GetRouteQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override bool IsCacheRequired => false;
public override async Task<Route> Query(GetRouteCommand command)
{
var query = DbContext.Routes
@@ -15,8 +15,6 @@ public ListRoutesQuery(EkShopContext dbContext)
: base (dbContext)
{ }
public override bool IsCacheRequired => false;
public override async Task<List<Route>> Query(ListRoutesCommand command)
{
var query = DbContext.Routes
@@ -7,6 +7,7 @@
using Ek.Shop.Base.Data.Queries.Queries;
using Ek.Shop.Base.Data.Queries.RemoteQueries;
using Ek.Shop.Base.Data.WorkContexts;
using Ek.Shop.Contracts.Abstractions;
using Ek.Shop.Data.Baskets;
using Ek.Shop.Data.Categories;
using Ek.Shop.Data.ClassifierStores;
@@ -293,8 +294,9 @@ private void InitializeContainer(IApplicationBuilder app)
container.Register(typeof(IQuery<,>), new[] { typeof(GetCategoryBaseQuery).Assembly });
// TODO: Temporary do not cache any queries because of lost EF model reference and impossibility to track decorated query model-result.
//container.RegisterDecorator(typeof(IRemoteQuery<,>),
// typeof(RemoteQueryCacheDecorator<,>));
container.RegisterDecorator(typeof(IRemoteQuery<,>),
typeof(RemoteQueryCacheDecorator<,>),
context => typeof(CachingCommand).IsAssignableFrom(context.ServiceType.GetGenericArguments()[0]));
// TODO: Refactor to use simpleInjector UseMiddleware
container.Register<WorkContextMiddleware>();

0 comments on commit bf935ee

Please sign in to comment.