diff --git a/lib/localization-provider b/lib/localization-provider index 4dc0b50..2e879c6 160000 --- a/lib/localization-provider +++ b/lib/localization-provider @@ -1 +1 @@ -Subproject commit 4dc0b50a246d66e4a1f0f0f7046f837880aaeff4 +Subproject commit 2e879c6f31da6729c197c951bde5c49d3636099b diff --git a/src/DbLocalizationProvider.AspNetCore/Cache/ClearCacheHandler.cs b/src/DbLocalizationProvider.AspNetCore/Cache/ClearCacheHandler.cs index 632f738..d5391e3 100644 --- a/src/DbLocalizationProvider.AspNetCore/Cache/ClearCacheHandler.cs +++ b/src/DbLocalizationProvider.AspNetCore/Cache/ClearCacheHandler.cs @@ -6,20 +6,18 @@ namespace DbLocalizationProvider.AspNetCore.Cache; -public class ClearCacheHandler : ICommandHandler +/// +/// Clears the cache. +/// +/// Cache implementation +public class ClearCacheHandler(ICacheManager cache) : ICommandHandler { - private readonly ICacheManager _cache; - - public ClearCacheHandler(ICacheManager cache) - { - _cache = cache; - } - + /// public void Execute(ClearCache.Command command) { - foreach (var itemToRemove in InMemoryCacheManager.Entries) + foreach (var itemToRemove in cache.Keys) { - _cache.Remove(itemToRemove.Key); + cache.Remove(itemToRemove); } } } diff --git a/src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCacheManager.cs b/src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCache.cs similarity index 55% rename from src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCacheManager.cs rename to src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCache.cs index 6aa9894..ed084d8 100644 --- a/src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCacheManager.cs +++ b/src/DbLocalizationProvider.AspNetCore/Cache/InMemoryCache.cs @@ -2,41 +2,43 @@ // Licensed under Apache-2.0. See the LICENSE file in the project root for more information using System.Collections.Concurrent; +using System.Collections.Generic; using DbLocalizationProvider.Cache; using Microsoft.Extensions.Caching.Memory; namespace DbLocalizationProvider.AspNetCore.Cache; -public class InMemoryCacheManager : ICacheManager +/// +/// Cache using IMemoryCache as a storage. +/// +public class InMemoryCache : ICache { - // this is used in cache helper to enumerate over known entries and remove what's needed - // implemented because there is no way to enumerate keys using built-in cache provider - internal static readonly ConcurrentDictionary Entries = new(); private readonly IMemoryCache _memCache; - public InMemoryCacheManager(IMemoryCache memCache) + /// + /// Creates new instance. + /// + /// Memory cache + public InMemoryCache(IMemoryCache memCache) { _memCache = memCache; } + /// public void Insert(string key, object value, bool insertIntoKnownResourceKeys) { _memCache.Set(key, value); - Entries.TryRemove(key, out _); - Entries.TryAdd(key, true); } + /// public object Get(string key) { return _memCache.Get(key); } + /// public void Remove(string key) { _memCache.Remove(key); - Entries.TryRemove(key, out _); } - - public event CacheEventHandler OnInsert; - public event CacheEventHandler OnRemove; } diff --git a/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/CacheHelper.cs b/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/CacheHelper.cs index b019efa..026de67 100644 --- a/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/CacheHelper.cs +++ b/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/CacheHelper.cs @@ -10,45 +10,43 @@ namespace DbLocalizationProvider.AspNetCore.ClientsideProvider; internal class CacheHelper { - private static readonly string _separator = "_|_"; + private const string Separator = "_|_"; public static string GenerateKey(string filename, string language, bool isDebugMode, bool camelCase) { - return $"{filename}{_separator}{language}__{(isDebugMode ? "debug" : "release")}__{camelCase}"; + return $"{filename}{Separator}{language}__{(isDebugMode ? "debug" : "release")}__{camelCase}"; } - public static string GetContainerName(string key) + public static string? GetContainerName(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } - return !key.Contains(_separator) ? null : key.Substring(0, key.IndexOf(_separator, StringComparison.Ordinal)); + return !key.Contains(Separator) ? null : key.Substring(0, key.IndexOf(Separator, StringComparison.Ordinal)); } public static void CacheManagerOnRemove(CacheEventArgs args, ICacheManager cache) { - // TODO: implement IEnumerable on cache manager - using (var existingKeys = InMemoryCacheManager.Entries.GetEnumerator()) + using var existingKeys = cache.Keys.GetEnumerator(); + var entriesToRemove = new List(); + + while (existingKeys.MoveNext()) { - var entriesToRemove = new List(); - while (existingKeys.MoveNext()) - { - var key = CacheKeyHelper.GetResourceKeyFromCacheKey(existingKeys.Current.Key); - var containerName = GetContainerName(key); - - if (containerName != null - && args.ResourceKey.StartsWith(containerName, StringComparison.InvariantCultureIgnoreCase)) - { - entriesToRemove.Add(existingKeys.Current.Key); - } - } + var key = CacheKeyHelper.GetResourceKeyFromCacheKey(existingKeys.Current); + var containerName = GetContainerName(key); - foreach (var entry in entriesToRemove) + if (containerName != null + && args.ResourceKey.StartsWith(containerName, StringComparison.InvariantCultureIgnoreCase)) { - cache.Remove(entry); + entriesToRemove.Add(existingKeys.Current); } } + + foreach (var entry in entriesToRemove) + { + cache.Remove(entry); + } } } diff --git a/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/RequestHandler.cs b/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/RequestHandler.cs index ae47dce..3d1f5ae 100644 --- a/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/RequestHandler.cs +++ b/src/DbLocalizationProvider.AspNetCore/ClientsideProvider/RequestHandler.cs @@ -87,9 +87,9 @@ private string GenerateResponse(HttpContext context) languageName, debugMode, camelCase, - context.RequestServices.GetService(), - context.RequestServices.GetService>(), - context.RequestServices.GetService()); + context.RequestServices.GetRequiredService(), + context.RequestServices.GetRequiredService>(), + context.RequestServices.GetRequiredService()); cache.Insert(cacheKey, responseObject, false); } diff --git a/src/DbLocalizationProvider.AspNetCore/IServiceCollectionExtensions.cs b/src/DbLocalizationProvider.AspNetCore/IServiceCollectionExtensions.cs index 79139b3..95f4ead 100644 --- a/src/DbLocalizationProvider.AspNetCore/IServiceCollectionExtensions.cs +++ b/src/DbLocalizationProvider.AspNetCore/IServiceCollectionExtensions.cs @@ -38,7 +38,7 @@ public static class IServiceCollectionExtensions /// public static IDbLocalizationProviderBuilder AddDbLocalizationProvider( this IServiceCollection services, - Action setup = null) + Action? setup = null) { var ctx = new ConfigurationContext(services); var factory = ctx.TypeFactory; @@ -50,14 +50,17 @@ public static IDbLocalizationProviderBuilder AddDbLocalizationProvider( // set to default in-memory provider // only if we have IMemoryCache service registered - if (services.FirstOrDefault(d => d.ServiceType == typeof(IMemoryCache)) != null) + var memCacheDescriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IMemoryCache)); + if (memCacheDescriptor is { ServiceType: not null }) { - services.AddSingleton(); + ctx._baseCacheManager.SetInnerManager(sp => new InMemoryCache((IMemoryCache)sp.GetRequiredService(memCacheDescriptor.ServiceType))); } // run custom configuration setup (if any) setup?.Invoke(ctx); + services.AddSingleton(ctx.CacheManager); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/DbLocalizationProvider.AspNetCore/IServiceProviderExtensions.cs b/src/DbLocalizationProvider.AspNetCore/IServiceProviderExtensions.cs index 39149ec..80be676 100644 --- a/src/DbLocalizationProvider.AspNetCore/IServiceProviderExtensions.cs +++ b/src/DbLocalizationProvider.AspNetCore/IServiceProviderExtensions.cs @@ -26,6 +26,13 @@ public static void UseDbLocalizationProvider(this IServiceProvider serviceFactor } var context = serviceFactory.GetRequiredService>(); + + // resolve inner cache (if set) + if (context.Value._baseCacheManager._implementationFactory != null) + { + context.Value._baseCacheManager.SetInnerManager(context.Value._baseCacheManager._implementationFactory(serviceFactory)); + } + var usageConfigurator = serviceFactory.GetService(); if (usageConfigurator != null) diff --git a/src/DbLocalizationProvider.AspNetCore/Queries/CachedGetAllResourcesHandler.cs b/src/DbLocalizationProvider.AspNetCore/Queries/CachedGetAllResourcesHandler.cs index ae2d32f..7c841e1 100644 --- a/src/DbLocalizationProvider.AspNetCore/Queries/CachedGetAllResourcesHandler.cs +++ b/src/DbLocalizationProvider.AspNetCore/Queries/CachedGetAllResourcesHandler.cs @@ -72,9 +72,12 @@ public IEnumerable Execute(GetAllResources.Query query) if (resourceFromDb != null) { - _configurationContext.Value.CacheManager.Insert(cacheKey, resourceFromDb, true); result.Add(resourceFromDb); } + + _configurationContext.Value.CacheManager.Insert(cacheKey, + resourceFromDb ?? LocalizationResource.CreateNonExisting(key), + true); } }