From c174243b8f1f91c0ce63203d320b7a6bf8af3fff Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Tue, 16 Jun 2020 21:00:03 +0200 Subject: [PATCH] Refactor of the ICacheManager interface --- Grand.Core/Caching/CacheExtensions.cs | 81 ------- Grand.Core/Caching/ICacheManager.cs | 20 +- Grand.Core/Caching/MemoryCacheManager.cs | 42 ++-- Grand.Core/Caching/PerRequestCacheManager.cs | 211 ------------------ .../Infrastructure/DependencyRegistrar.cs | 1 - Grand.Services/Discounts/DiscountService.cs | 14 +- Grand.Web/Controllers/InstallController.cs | 2 +- .../GetCategoryFeaturedProductsHandler.cs | 11 +- .../Handlers/Catalog/GetCategoryHandler.cs | 12 +- .../GetManufacturerFeaturedProductsHandler.cs | 12 +- .../Catalog/GetManufacturerHandler.cs | 14 +- .../Caching/MemoryCacheManagerTests.cs | 2 +- Tests/Grand.Core.Tests/ExtensionsTests.cs | 4 +- .../Catalog/CategoryServiceTests.cs | 10 - .../Discounts/DiscountServiceTests.cs | 2 +- 15 files changed, 50 insertions(+), 388 deletions(-) delete mode 100644 Grand.Core/Caching/CacheExtensions.cs delete mode 100644 Grand.Core/Caching/PerRequestCacheManager.cs diff --git a/Grand.Core/Caching/CacheExtensions.cs b/Grand.Core/Caching/CacheExtensions.cs deleted file mode 100644 index 79f1e789f2..0000000000 --- a/Grand.Core/Caching/CacheExtensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Grand.Core.Caching -{ - /// - /// Extensions - /// - public static class CacheExtensions - { - /// - /// Get a cached item. If it's not in the cache yet, then load and cache it - /// - /// Type - /// Cache manager - /// Cache key - /// Function to load item if it's not in the cache yet - /// Cached item - public static Task GetAsync(this ICacheManager cacheManager, string key, Func> acquire) - { - return GetAsync(cacheManager, key, CommonHelper.CacheTimeMinutes, acquire); - } - - /// - /// Get a cached item. If it's not in the cache yet, then load and cache it - /// - /// Type - /// Cache manager - /// Cache key - /// Cache time in minutes (0 - do not cache) - /// Function to load item if it's not in the cache yet - /// Cached item - public static async Task GetAsync(this ICacheManager cacheManager, string key, int cacheTime, Func> acquire) - { - var value = await cacheManager.TryGetValueAsync(key); - if (value.FromCache == true) - return value.Result; - - var result = await acquire(); - if (cacheTime > 0) - await cacheManager.SetAsync(key, result, cacheTime); - return result; - } - - /// - /// Get a cached item. If it's not in the cache yet, then load and cache it - /// - /// Type - /// Cache manager - /// Cache key - /// Cache time in minutes (0 - do not cache) - /// Function to load item if it's not in the cache yet - /// Cached item - public static T Get(this ICacheManager cacheManager, string key, int cacheTime, Func acquire) - { - var value = cacheManager.TryGetValue(key); - if (value.FromCache == true) - return value.Result; - - var result = acquire(); - if (cacheTime > 0) - cacheManager.Set(key, result, cacheTime); - - return result; - } - - /// - /// Get a cached item. If it's not in the cache yet, then load and cache it - /// - /// Type - /// Cache manager - /// Cache key - /// Cache time in minutes (0 - do not cache) - /// Function to load item if it's not in the cache yet - /// Cached item - public static T Get(this ICacheManager cacheManager, string key, Func acquire) - { - return Get(cacheManager, key, CommonHelper.CacheTimeMinutes, acquire); - } - } -} diff --git a/Grand.Core/Caching/ICacheManager.cs b/Grand.Core/Caching/ICacheManager.cs index c1f8d65fc7..d3f187d473 100644 --- a/Grand.Core/Caching/ICacheManager.cs +++ b/Grand.Core/Caching/ICacheManager.cs @@ -13,30 +13,18 @@ public interface ICacheManager: IDisposable /// /// Type /// The key of the value to get. + /// Function to load /// The value associated with the specified key. - Task GetAsync(string key); + Task GetAsync(string key, Func> acquire); /// /// Gets or sets the value associated with the specified key. /// /// Type /// The key of the value to get. + /// Function to load /// The value associated with the specified key. - T Get(string key); - - /// - /// Gets or sets the value associated with the specified key asynchronosly - /// - /// The key of the value to get. - /// The value associated with the specified key. - Task<(T Result, bool FromCache)> TryGetValueAsync(string key); - - /// - /// Gets or sets the value associated with the specified key synchronosly - /// - /// The key of the value to get. - /// The value associated with the specified key. - (T Result, bool FromCache) TryGetValue(string key); + T Get(string key, Func acquire); /// /// Adds the specified key and object to the cache. diff --git a/Grand.Core/Caching/MemoryCacheManager.cs b/Grand.Core/Caching/MemoryCacheManager.cs index 8066439788..c484185750 100644 --- a/Grand.Core/Caching/MemoryCacheManager.cs +++ b/Grand.Core/Caching/MemoryCacheManager.cs @@ -144,10 +144,16 @@ private void PostEviction(object key, object value, EvictionReason reason, objec /// /// Type of cached item /// Key of cached item + /// Function to load /// The cached value associated with the specified key - public virtual Task GetAsync(string key) + public virtual async Task GetAsync(string key, Func> acquire) { - return Task.FromResult(_cache.Get(key)); + return await _cache.GetOrCreateAsync(key, entry => + { + AddKey(key); + entry.SetOptions(GetMemoryCacheEntryOptions(CommonHelper.CacheTimeMinutes)); + return acquire(); + }); } /// @@ -155,35 +161,19 @@ public virtual Task GetAsync(string key) /// /// Type of cached item /// Key of cached item + /// Function to load /// The cached value associated with the specified key - public T Get(string key) - { - return _cache.Get(key); - } - - /// - /// Gets or sets the value associated with the specified key. - /// - /// Key of cached item - /// The cached value associated with the specified key - public virtual (T, bool) TryGetValue(string key) + public T Get(string key, Func acquire) { - if (_cache.TryGetValue(key, out T value)) + return _cache.GetOrCreate(key, entry => { - return (value, true); - } - return (default(T), false); + AddKey(key); + entry.SetOptions(GetMemoryCacheEntryOptions(CommonHelper.CacheTimeMinutes)); + return acquire(); + }); } - /// - /// Gets or sets the value associated with the specified key. - /// - /// Key of cached item - /// The cached value associated with the specified key - public Task<(T Result, bool FromCache)> TryGetValueAsync(string key) - { - return Task.FromResult(TryGetValue(key)); - } + /// /// Adds the specified key and object to the cache diff --git a/Grand.Core/Caching/PerRequestCacheManager.cs b/Grand.Core/Caching/PerRequestCacheManager.cs deleted file mode 100644 index 67c49529de..0000000000 --- a/Grand.Core/Caching/PerRequestCacheManager.cs +++ /dev/null @@ -1,211 +0,0 @@ -using Grand.Core.Extensions; -using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Grand.Core.Caching -{ - /// - /// Represents a manager for caching during an HTTP request (short term caching) - /// - public partial class PerRequestCacheManager : ICacheManager - { - #region Fields - - private readonly IHttpContextAccessor _httpContextAccessor; - - #endregion - - #region Ctor - - /// - /// Gets a key/value collection that can be used to share data within the scope of this request - /// - public PerRequestCacheManager(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - #endregion - - #region Utilities - - /// - /// Gets a key/value collection that can be used to share data within the scope of this request - /// - protected virtual IDictionary GetItems() - { - return _httpContextAccessor.HttpContext?.Items; - } - - #endregion - - #region Methods - /// - /// Gets or sets the value associated with the specified key. - /// - /// Type of cached item - /// Key of cached item - /// The cached value associated with the specified key - public virtual Task GetAsync(string key) - { - var items = GetItems(); - if (items == null) - return Task.FromResult(default(T)); - - return Task.FromResult((T)items[key]); - } - - /// - /// Gets or sets the value associated with the specified key. - /// - /// Type of cached item - /// Key of cached item - /// The cached value associated with the specified key - public T Get(string key) - { - var items = GetItems(); - if (items == null) - return default(T); - - return (T)items[key]; - } - - /// - /// Gets or sets the value associated with the specified key. - /// - /// Type of cached item - /// Key of cached item - /// The cached value associated with the specified key - public virtual (T, bool) TryGetValue(string key) - { - var items = GetItems(); - if (items?[key] == null) - return (default(T), false); - - return ((T)items[key], true); - } - - public Task<(T Result, bool FromCache)> TryGetValueAsync(string key) - { - return Task.FromResult(TryGetValue(key)); - } - - /// - /// Adds the specified key and object to the cache - /// - /// Key of cached item - /// Value for caching - /// Cache time in minutes - public virtual Task SetAsync(string key, object data, int cacheTime) - { - var items = GetItems(); - if (items == null) - return Task.CompletedTask; - - if (data != null) - items[key] = data; - - return Task.CompletedTask; - } - - /// - /// Adds the specified key and object to the cache - /// - /// Key of cached item - /// Value for caching - /// Cache time in minutes - public void Set(string key, object data, int cacheTime) - { - if (data == null) - { - return; - } - - var items = GetItems(); - if (items == null) - return; - - items[key] = data; - } - - /// - /// Gets a value indicating whether the value associated with the specified key is cached - /// - /// Key of cached item - /// True if item already is in cache; otherwise false - public virtual bool IsSet(string key) - { - var items = GetItems(); - - return items?[key] != null; - } - - /// - /// Removes the value with the specified key from the cache - /// - /// Key of cached item - public virtual Task RemoveAsync(string key, bool publisher = true) - { - var items = GetItems(); - - items?.Remove(key); - return Task.CompletedTask; - } - - /// - /// Removes items by key prefix - /// - /// String prefix - /// publisher - public virtual Task RemoveByPrefix(string prefix, bool publisher = true) - { - var items = GetItems(); - if (items == null) - return Task.CompletedTask; - - var keysToRemove = items.Keys.Where(x => x.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList(); - foreach (var key in keysToRemove) - { - items.Remove(key); - } - return Task.CompletedTask; - } - - /// - /// Removes items by key prefix - /// - /// String prefix - /// publisher - /// publisher - public Task RemoveByPrefixAsync(string prefix, bool publisher = true) - { - return RemoveByPrefix(prefix); - } - - /// - /// Clear all cache data - /// - /// publisher - public virtual Task Clear(bool publisher = true) - { - var items = GetItems(); - - items?.Clear(); - - return Task.CompletedTask; - } - - /// - /// Dispose cache manager - /// - public virtual void Dispose() - { - //nothing special - } - - #endregion - } -} \ No newline at end of file diff --git a/Grand.Framework/Infrastructure/DependencyRegistrar.cs b/Grand.Framework/Infrastructure/DependencyRegistrar.cs index d18907c2f1..71a1eecbb3 100644 --- a/Grand.Framework/Infrastructure/DependencyRegistrar.cs +++ b/Grand.Framework/Infrastructure/DependencyRegistrar.cs @@ -186,7 +186,6 @@ private void RegisterDataLayer(ContainerBuilder builder) private void RegisterCache(ContainerBuilder builder, GrandConfig config) { - builder.RegisterType().InstancePerLifetimeScope(); builder.RegisterType().As().SingleInstance(); if (config.RedisPubSubEnabled) { diff --git a/Grand.Services/Discounts/DiscountService.cs b/Grand.Services/Discounts/DiscountService.cs index 4fda9699de..99100da6bc 100644 --- a/Grand.Services/Discounts/DiscountService.cs +++ b/Grand.Services/Discounts/DiscountService.cs @@ -84,7 +84,6 @@ public partial class DiscountService : IDiscountService private readonly IStoreContext _storeContext; private readonly IPluginFinder _pluginFinder; private readonly IMediator _mediator; - private readonly PerRequestCacheManager _perRequestCache; private readonly ShoppingCartSettings _shoppingCartSettings; private readonly CatalogSettings _catalogSettings; @@ -107,7 +106,6 @@ public partial class DiscountService : IDiscountService IRepository categoryRepository, IRepository manufacturerRepository, IRepository vendorRepository, - PerRequestCacheManager perRequestCache, ShoppingCartSettings shoppingCartSettings, CatalogSettings catalogSettings ) @@ -124,7 +122,6 @@ CatalogSettings catalogSettings _categoryRepository = categoryRepository; _manufacturerRepository = manufacturerRepository; _vendorRepository = vendorRepository; - _perRequestCache = perRequestCache; _shoppingCartSettings = shoppingCartSettings; _catalogSettings = catalogSettings; } @@ -555,9 +552,9 @@ public virtual async Task ValidateDiscount(Discount di //invalid by default - string key = $"DiscountValidationResult_{customer.Id}_{discount.Id}_{string.Join("_", couponCodesToValidate)}"; - var validationResult = await _perRequestCache.GetAsync(key, async () => - { + //string key = $"DiscountValidationResult_{customer.Id}_{discount.Id}_{string.Join("_", couponCodesToValidate)}"; + //var validationResult = await _perRequestCache.GetAsync(key, async () => + //{ var result = new DiscountValidationResult(); //check is enabled @@ -702,9 +699,10 @@ public virtual async Task ValidateDiscount(Discount di result.IsValid = true; return result; - }); - return validationResult; + // }); + + //return validationResult; } /// /// Gets a discount usage history record diff --git a/Grand.Web/Controllers/InstallController.cs b/Grand.Web/Controllers/InstallController.cs index 4be16f2848..9e4f1c9c3f 100644 --- a/Grand.Web/Controllers/InstallController.cs +++ b/Grand.Web/Controllers/InstallController.cs @@ -68,7 +68,7 @@ public virtual async Task Index() var locService = _serviceProvider.GetRequiredService(); - var installed = await _cacheManager.GetAsync("Installed"); + var installed = await _cacheManager.GetAsync("Installed", async () => { return await Task.FromResult(false); }); if (installed) return View(new InstallModel() { Installed = true }); diff --git a/Grand.Web/Features/Handlers/Catalog/GetCategoryFeaturedProductsHandler.cs b/Grand.Web/Features/Handlers/Catalog/GetCategoryFeaturedProductsHandler.cs index abb5513fba..3ed4918af4 100644 --- a/Grand.Web/Features/Handlers/Catalog/GetCategoryFeaturedProductsHandler.cs +++ b/Grand.Web/Features/Handlers/Catalog/GetCategoryFeaturedProductsHandler.cs @@ -81,11 +81,9 @@ public async Task> Handle(GetCategoryFeaturedProducts reque IPagedList featuredProducts = null; string cacheKey = string.Format(ModelCacheEventConst.CATEGORY_HAS_FEATURED_PRODUCTS_KEY, item.Id, string.Join(",", request.Customer.GetCustomerRoleIds()), request.Store.Id); - var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey); - if (!hasFeaturedProductsCache.HasValue) + + var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey, async () => { - //no value in the cache yet - //let's load products and cache the result (true/false) featuredProducts = (await _mediator.Send(new GetSearchProductsQuery() { PageSize = _catalogSettings.LimitOfFeaturedProducts, CategoryIds = new List { item.Id }, @@ -94,10 +92,9 @@ public async Task> Handle(GetCategoryFeaturedProducts reque VisibleIndividuallyOnly = true, FeaturedProducts = true })).products; + return featuredProducts.Any(); + }); - hasFeaturedProductsCache = featuredProducts.Any(); - await _cacheManager.SetAsync(cacheKey, hasFeaturedProductsCache, CommonHelper.CacheTimeMinutes); - } if (hasFeaturedProductsCache.Value && featuredProducts == null) { //cache indicates that the category has featured products diff --git a/Grand.Web/Features/Handlers/Catalog/GetCategoryHandler.cs b/Grand.Web/Features/Handlers/Catalog/GetCategoryHandler.cs index cf6d360b62..4709cb8d3d 100644 --- a/Grand.Web/Features/Handlers/Catalog/GetCategoryHandler.cs +++ b/Grand.Web/Features/Handlers/Catalog/GetCategoryHandler.cs @@ -157,11 +157,9 @@ public async Task Handle(GetCategory request, CancellationToken c IPagedList featuredProducts = null; string cacheKey = string.Format(ModelCacheEventConst.CATEGORY_HAS_FEATURED_PRODUCTS_KEY, request.Category.Id, string.Join(",", customer.GetCustomerRoleIds()), storeId); - var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey); - if (!hasFeaturedProductsCache.HasValue) + + var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey, async () => { - //no value in the cache yet - //let's load products and cache the result (true/false) featuredProducts = (await _mediator.Send(new GetSearchProductsQuery() { PageSize = _catalogSettings.LimitOfFeaturedProducts, CategoryIds = new List { request.Category.Id }, @@ -170,9 +168,9 @@ public async Task Handle(GetCategory request, CancellationToken c VisibleIndividuallyOnly = true, FeaturedProducts = true })).products; - hasFeaturedProductsCache = featuredProducts.Any(); - await _cacheManager.SetAsync(cacheKey, hasFeaturedProductsCache, CommonHelper.CacheTimeMinutes); - } + return featuredProducts.Any(); + }); + if (hasFeaturedProductsCache.Value && featuredProducts == null) { //cache indicates that the category has featured products diff --git a/Grand.Web/Features/Handlers/Catalog/GetManufacturerFeaturedProductsHandler.cs b/Grand.Web/Features/Handlers/Catalog/GetManufacturerFeaturedProductsHandler.cs index a8d8bba10d..a11c4044e0 100644 --- a/Grand.Web/Features/Handlers/Catalog/GetManufacturerFeaturedProductsHandler.cs +++ b/Grand.Web/Features/Handlers/Catalog/GetManufacturerFeaturedProductsHandler.cs @@ -84,11 +84,9 @@ public async Task> Handle(GetManufacturerFeaturedProduc item.Id, string.Join(",", request.Customer.GetCustomerRoleIds()), request.Store.Id); - var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey); - if (!hasFeaturedProductsCache.HasValue) + + var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey, async () => { - //no value in the cache yet - //let's load products and cache the result (true/false) featuredProducts = (await _mediator.Send(new GetSearchProductsQuery() { PageSize = _catalogSettings.LimitOfFeaturedProducts, ManufacturerId = item.Id, @@ -97,9 +95,9 @@ public async Task> Handle(GetManufacturerFeaturedProduc VisibleIndividuallyOnly = true, FeaturedProducts = true })).products; - hasFeaturedProductsCache = featuredProducts.Any(); - await _cacheManager.SetAsync(cacheKey, hasFeaturedProductsCache, CommonHelper.CacheTimeMinutes); - } + return featuredProducts.Any(); + }); + if (hasFeaturedProductsCache.Value && featuredProducts == null) { //cache indicates that the manufacturer has featured products diff --git a/Grand.Web/Features/Handlers/Catalog/GetManufacturerHandler.cs b/Grand.Web/Features/Handlers/Catalog/GetManufacturerHandler.cs index 115c44f7ba..f8a21a045c 100644 --- a/Grand.Web/Features/Handlers/Catalog/GetManufacturerHandler.cs +++ b/Grand.Web/Features/Handlers/Catalog/GetManufacturerHandler.cs @@ -93,12 +93,9 @@ public async Task Handle(GetManufacturer request, Cancellatio request.Manufacturer.Id, string.Join(",", request.Customer.GetCustomerRoleIds()), request.Store.Id); - var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey); - if (!hasFeaturedProductsCache.HasValue) - { - //no value in the cache yet - //let's load products and cache the result (true/false) - featuredProducts = (await _mediator.Send(new GetSearchProductsQuery() { + var hasFeaturedProductsCache = await _cacheManager.GetAsync(cacheKey, async () => + { + var featuredProducts = (await _mediator.Send(new GetSearchProductsQuery() { PageSize = _catalogSettings.LimitOfFeaturedProducts, ManufacturerId = request.Manufacturer.Id, Customer = request.Customer, @@ -106,9 +103,8 @@ public async Task Handle(GetManufacturer request, Cancellatio VisibleIndividuallyOnly = true, FeaturedProducts = true })).products; - hasFeaturedProductsCache = featuredProducts.Any(); - await _cacheManager.SetAsync(cacheKey, hasFeaturedProductsCache, CommonHelper.CacheTimeMinutes); - } + return featuredProducts.Any(); + }); if (hasFeaturedProductsCache.Value && featuredProducts == null) { //cache indicates that the manufacturer has featured products diff --git a/Tests/Grand.Core.Tests/Caching/MemoryCacheManagerTests.cs b/Tests/Grand.Core.Tests/Caching/MemoryCacheManagerTests.cs index 299828d0f0..96e6d4d477 100644 --- a/Tests/Grand.Core.Tests/Caching/MemoryCacheManagerTests.cs +++ b/Tests/Grand.Core.Tests/Caching/MemoryCacheManagerTests.cs @@ -20,7 +20,7 @@ public async Task Set_and_get_example_data_by_passing_specified_key() MemoryCacheManager memoryCacheManager = new MemoryCacheManager(new MemoryCache(new MemoryCacheOptions { }), eventPublisher.Object); await memoryCacheManager.SetAsync(key, data, cacheTime); - Assert.AreEqual(await memoryCacheManager.GetAsync(key), data); + Assert.AreEqual(await memoryCacheManager.GetAsync(key, async () => { return await Task.FromResult(new byte()); }),data); } [TestMethod()] diff --git a/Tests/Grand.Core.Tests/ExtensionsTests.cs b/Tests/Grand.Core.Tests/ExtensionsTests.cs index 67e61b1ffd..d960ec8928 100644 --- a/Tests/Grand.Core.Tests/ExtensionsTests.cs +++ b/Tests/Grand.Core.Tests/ExtensionsTests.cs @@ -46,8 +46,8 @@ public async Task RemoveByPatternTest() await icacheManager.RemoveByPrefix(pattern); - Assert.IsNotNull(icacheManager.GetAsync("key1202")); - Assert.IsNotNull(icacheManager.GetAsync("key1204")); + Assert.IsNotNull(icacheManager.GetAsync("key1202", async () => { return await Task.FromResult(0); })); + Assert.IsNotNull(icacheManager.GetAsync("key1204", async () => { return await Task.FromResult(0); })); } } } \ No newline at end of file diff --git a/Tests/Grand.Services.Tests/Catalog/CategoryServiceTests.cs b/Tests/Grand.Services.Tests/Catalog/CategoryServiceTests.cs index 976ed67147..9c58fff44e 100644 --- a/Tests/Grand.Services.Tests/Catalog/CategoryServiceTests.cs +++ b/Tests/Grand.Services.Tests/Catalog/CategoryServiceTests.cs @@ -85,16 +85,6 @@ public async Task UpdateCategory_ValidArgument_InvokeRepositoryAndCache() _casheManagerMock.Verify(c => c.RemoveByPrefix(It.IsAny(), true), Times.Exactly(3)); } - [TestMethod()] - public async Task GetCategoryById_ValidArgument_InvokeRepositoryAndCache() - { - var categoryId = "id"; - var cacheKey = string.Format("Grand.category.id-{0}", categoryId); - await _categoryService.GetCategoryById(categoryId); - _casheManagerMock.Verify(c => c.TryGetValueAsync( cacheKey), Times.Once); - _categoryRepositoryMock.Verify(c => c.GetByIdAsync("id"), Times.Once); - } - [TestMethod()] public void GetCategoryBreadCrumb_ShouldReturnEmptyList() { diff --git a/Tests/Grand.Services.Tests/Discounts/DiscountServiceTests.cs b/Tests/Grand.Services.Tests/Discounts/DiscountServiceTests.cs index 3bea832408..db97942d61 100644 --- a/Tests/Grand.Services.Tests/Discounts/DiscountServiceTests.cs +++ b/Tests/Grand.Services.Tests/Discounts/DiscountServiceTests.cs @@ -90,7 +90,7 @@ public class DiscountServiceTests { _discountService = new DiscountService(new TestMemoryCacheManager(new Mock().Object, _eventPublisher), _discountRepo, _discountCouponRepo, _discountUsageHistoryRepo, _localizationService, _storeContext, - new PluginFinder(_serviceProvider), _eventPublisher, extraProductRepo, extraCategoryRepo, extraManufacturerRepo, extraVendorRepo, new PerRequestCacheManager(null), + new PluginFinder(_serviceProvider), _eventPublisher, extraProductRepo, extraCategoryRepo, extraManufacturerRepo, extraVendorRepo, _shoppingCartSettings, _catalogSettings); }