139 lines
4.6 KiB
C#
139 lines
4.6 KiB
C#
using Microsoft.Extensions.Caching.Memory;
|
|
using System.Collections.Concurrent;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace BankSampahApp.Services;
|
|
|
|
/// <summary>
|
|
/// Implementation of cache service dengan enhanced functionality
|
|
/// </summary>
|
|
public class CacheService : ICacheService
|
|
{
|
|
private readonly IMemoryCache _cache;
|
|
private readonly ILogger<CacheService> _logger;
|
|
private readonly ConcurrentDictionary<string, bool> _cacheKeys;
|
|
|
|
public CacheService(IMemoryCache cache, ILogger<CacheService> logger)
|
|
{
|
|
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
_cacheKeys = new ConcurrentDictionary<string, bool>();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> provider, TimeSpan expiration, CacheItemPriority priority = CacheItemPriority.Normal)
|
|
{
|
|
if (_cache.TryGetValue(key, out T? cachedValue))
|
|
{
|
|
_logger.LogDebug("Cache hit for key: {CacheKey}", key);
|
|
return cachedValue!;
|
|
}
|
|
|
|
_logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", key);
|
|
|
|
var data = await provider();
|
|
Set(key, data, expiration, priority);
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<T> GetOrSetWithSlidingAsync<T>(string key, Func<Task<T>> provider, TimeSpan slidingExpiration, TimeSpan absoluteExpiration, CacheItemPriority priority = CacheItemPriority.Normal)
|
|
{
|
|
if (_cache.TryGetValue(key, out T? cachedValue))
|
|
{
|
|
_logger.LogDebug("Cache hit for key: {CacheKey}", key);
|
|
return cachedValue!;
|
|
}
|
|
|
|
_logger.LogDebug("Cache miss for key: {CacheKey}, fetching fresh data", key);
|
|
|
|
var data = await provider();
|
|
|
|
var cacheOptions = new MemoryCacheEntryOptions
|
|
{
|
|
SlidingExpiration = slidingExpiration,
|
|
AbsoluteExpirationRelativeToNow = absoluteExpiration,
|
|
Priority = priority,
|
|
PostEvictionCallbacks = { new PostEvictionCallbackRegistration
|
|
{
|
|
EvictionCallback = (key, value, reason, state) =>
|
|
{
|
|
_cacheKeys.TryRemove(key.ToString()!, out _);
|
|
_logger.LogDebug("Cache entry evicted: {Key}, Reason: {Reason}", key, reason);
|
|
}
|
|
}}
|
|
};
|
|
|
|
_cache.Set(key, data, cacheOptions);
|
|
_cacheKeys.TryAdd(key, true);
|
|
|
|
_logger.LogDebug("Cached data with sliding expiration for key: {CacheKey}", key);
|
|
|
|
return data;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public T? Get<T>(string key)
|
|
{
|
|
if (_cache.TryGetValue(key, out T? value))
|
|
{
|
|
_logger.LogDebug("Retrieved cached value for key: {CacheKey}", key);
|
|
return value;
|
|
}
|
|
|
|
_logger.LogDebug("Cache miss for key: {CacheKey}", key);
|
|
return default;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Set<T>(string key, T value, TimeSpan expiration, CacheItemPriority priority = CacheItemPriority.Normal)
|
|
{
|
|
var cacheOptions = new MemoryCacheEntryOptions
|
|
{
|
|
AbsoluteExpirationRelativeToNow = expiration,
|
|
Priority = priority,
|
|
PostEvictionCallbacks = { new PostEvictionCallbackRegistration
|
|
{
|
|
EvictionCallback = (key, value, reason, state) =>
|
|
{
|
|
_cacheKeys.TryRemove(key.ToString()!, out _);
|
|
_logger.LogDebug("Cache entry evicted: {Key}, Reason: {Reason}", key, reason);
|
|
}
|
|
}}
|
|
};
|
|
|
|
_cache.Set(key, value, cacheOptions);
|
|
_cacheKeys.TryAdd(key, true);
|
|
|
|
_logger.LogDebug("Cached value for key: {CacheKey}, Expiration: {Expiration}", key, expiration);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Remove(string key)
|
|
{
|
|
_cache.Remove(key);
|
|
_cacheKeys.TryRemove(key, out _);
|
|
_logger.LogDebug("Removed cache entry for key: {CacheKey}", key);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void RemoveByPattern(string pattern)
|
|
{
|
|
var regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
var keysToRemove = _cacheKeys.Keys.Where(key => regex.IsMatch(key)).ToList();
|
|
|
|
foreach (var key in keysToRemove)
|
|
{
|
|
Remove(key);
|
|
}
|
|
|
|
_logger.LogDebug("Removed {Count} cache entries matching pattern: {Pattern}", keysToRemove.Count, pattern);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool Exists(string key)
|
|
{
|
|
return _cacheKeys.ContainsKey(key);
|
|
}
|
|
} |