You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
HybridCache: Alternative Cache Invalidation Pattern Implementation
Problem Description
HybridCache's built-in tag-based cache removal was not functioning as expected, requiring an alternative approach for managing cache invalidation across services and operations.
Solution Overview
Implemented a custom two-layer caching system that provides better control over cache invalidation and maintains consistency across related operations.
Implementation
1. Base Cache Layer (NvCacheHybrid)
usingMicrosoft.Extensions.Caching.Hybrid;usingSystem.Collections.Concurrent;publicinterfaceINvCacheHybrid{ValueTask<T>GetOrCreateAsync<T>(stringGroupKey,Func<Task<T>>factory,dynamic?[]deps);ValueTask<T>GetOrCreateAsync<T>(stringGroupKey,Func<Task<T>>factory,dynamic?[]deps,TimeSpanduration);TaskRemove(stringkey);TaskRemove(string[]keys);TaskRemoveAll();}publicclassNvCacheHybrid:INvCacheHybrid{privatereadonlyHybridCachecache;privateConcurrentDictionary<string,ConcurrentBag<string>>KeysCache=newConcurrentDictionary<string,ConcurrentBag<string>>();publicNvCacheHybrid(HybridCachecache){this.cache=cache;}privatestringGetKeys(stringmainKey,dynamic?[]keys){vargetkey=string.Join("_",keys.Select(item =>$"{item??"_null_"}"));varkeyList=KeysCache.GetOrAdd(mainKey, _ =>newConcurrentBag<string>());keyList.Add(getkey);KeysCache.GetOrAdd("AppKeyCache", _ =>newConcurrentBag<string>()).Add(getkey);returngetkey;}publicasyncValueTask<T>GetOrCreateAsync<T>(stringGroupKey,Func<Task<T>>factory,dynamic?[]deps){returnawaitcache.GetOrCreateAsync<T>(GetKeys(GroupKey,deps),async Task =>awaitfactory());}publicasyncValueTask<T>GetOrCreateAsync<T>(stringGroupKey,Func<Task<T>>factory,dynamic?[]deps,TimeSpanduration){varoptions=newHybridCacheEntryOptions(){Expiration=duration,LocalCacheExpiration=duration};returnawaitcache.GetOrCreateAsync<T>(GetKeys(GroupKey,deps),async Task =>awaitfactory(),options:options);}publicasyncTaskRemove(string[]keys){varkeysToRemove=keys.Where(predicate:KeysCache.ContainsKey).SelectMany(key =>KeysCache[key]).Distinct();awaitcache.RemoveAsync(keysToRemove);}publicasyncTaskRemove(stringkey){if(KeysCache.TryGetValue(key,outvarcacheKeys)){awaitcache.RemoveAsync(cacheKeys);}}publicasyncTaskRemoveAll(){if(KeysCache.TryGetValue("AppKeyCache",outvarallKeys)){awaitcache.RemoveAsync(allKeys);}}}
### 2.Service Cache Layer (ICacheHybrid<TClass>)
```csharppublicinterfaceICacheHybrid<outTClass>{ValueTask<T>GetOrCreateAsync<T>(Func<Task<T>>factory,dynamic?[]deps);ValueTask<T>GetOrCreateAsync<T>(Func<Task<T>>factory,dynamic?[]deps,TimeSpanduration);TaskRemoveCache();TaskRemoveCache(CacheKeykey);TaskRemoveCache(CacheKey[]keys);TaskRemoveCacheAll();}publicclassNvServiceCache<TClass>:ICacheHybrid<TClass>{privatereadonlyINvCacheHybridcache;privatereadonlystringMainKey;publicNvServiceCache(INvCacheHybridcache){this.cache=cache;this.MainKey=typeof(TClass).Name;}publicasyncValueTask<T>GetOrCreateAsync<T>(Func<Task<T>>factory,dynamic?[]deps){returnawaitcache.GetOrCreateAsync(MainKey,factory,deps);}publicasyncValueTask<T>GetOrCreateAsync<T>(Func<Task<T>>factory,dynamic?[]deps,TimeSpanduration){returnawaitcache.GetOrCreateAsync<T>(MainKey,factory,deps,duration);}publicasyncTaskRemoveCache(){awaitcache.Remove(MainKey);}publicasyncTaskRemoveCache(CacheKeykey){awaitcache.Remove(key.ToString());}publicasyncTaskRemoveCache(CacheKey[]keys){vargetkeys=keys.Select(x =>x.ToString());awaitcache.Remove(getkeys.ToArray());}publicasyncTaskRemoveCacheAll(){awaitcache.RemoveAll();}}
### 3. Example Implementation(LevantamientoActivosByUbicacionService)
```csharppublicclassLevantamientoActivosByUbicacionService:ILevantamientoActivosByUbicacionService{privatereadonlyISqlDataAccesssql;privatereadonlyICacheHybrid<LevantamientoActivosByUbicacionService>cache;publicLevantamientoActivosByUbicacionService(ISqlDataAccesssql,ICacheHybrid<LevantamientoActivosByUbicacionService>cache){this.sql=sql;this.cache=cache;}publicasyncTask<IEnumerable<LevantamientoActivosTpDto>>ActivosObtenerByUbicacionParaInforme(intIdUbicacionActivo)=>awaitcache.GetOrCreateAsync(()=>sql.QueryAsync<LevantamientoActivosTpDto>("cont.ActivosObtenerByUbicacionParaInforme"),[nameof(ActivosObtenerByUbicacionParaInforme),IdUbicacionActivo]);publicasyncTask<DBEntity>LevantamientoActivoActualizarUbicacion(LevantamientoActivosTpDtoentity,stringUsuarioModifica){varresult=awaitsql.ExecuteAsync("cont.LevantamientoActivoActualizarUbicacion",new{entity.IdActivoFijos,entity.IdUbicacionActivo,UsuarioModifica});awaitcache.RemoveCache();awaitcache.RemoveCache([CacheKey.ActivoFijosService]);returnresult;}// Otros métodos del servicio...}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
HybridCache: Alternative Cache Invalidation Pattern Implementation
Problem Description
HybridCache's built-in tag-based cache removal was not functioning as expected, requiring an alternative approach for managing cache invalidation across services and operations.
Solution Overview
Implemented a custom two-layer caching system that provides better control over cache invalidation and maintains consistency across related operations.
Implementation
1. Base Cache Layer (NvCacheHybrid)
4. Dependency Injection Setup
Key Features
Thread-safe Key Management:
Hierarchical Cache Control:
Flexible Cache Duration:
Automatic Key Tracking:
Usage Notes
Cache Key Generation:
Cache Invalidation:
Performance Considerations:
Migration Notes
When migrating from tag-based caching:
Remember to maintain the CacheKey enum with all service identifiers for cross-service cache invalidation.
Beta Was this translation helpful? Give feedback.
All reactions