139 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
	
using Microsoft.Extensions.Caching.Memory;
 | 
						|
using System.Collections.Concurrent;
 | 
						|
using System.Text.RegularExpressions;
 | 
						|
 | 
						|
namespace BpsRwApp.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);
 | 
						|
    }
 | 
						|
} |