using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Haoliang.Core.Services;
using Haoliang.Models.Device;
using Haoliang.Models.Production;
using Haoliang.Models.System;
namespace Haoliang.Core.Services
{
public interface ICacheService
{
///
/// Get cached value or execute factory if not exists
///
Task GetOrSetAsync(string key, Func> factory, MemoryCacheEntryOptions options = null);
///
/// Get cached value synchronously
///
T Get(string key);
///
/// Set cache value
///
void Set(string key, T value, MemoryCacheEntryOptions options = null);
///
/// Remove cached value
///
bool Remove(string key);
///
/// Check if key exists in cache
///
bool Exists(string key);
///
/// Clear all cache
///
void Clear();
///
/// Get cache statistics
///
CacheStatistics GetStatistics();
///
/// Get cache keys matching pattern
///
IEnumerable GetKeys(string pattern);
///
/// Refresh cached value
///
bool Refresh(string key);
}
public class CacheStatistics
{
public long TotalItems { get; set; }
public long Hits { get; set; }
public long Misses { get; set; }
public double HitRate => Hits + Misses > 0 ? (double)Hits / (Hits + Misses) : 0;
public long MemoryUsageBytes { get; set; }
public DateTime LastCleared { get; set; }
public Dictionary ItemsByType { get; set; }
}
public class MemoryCacheService : ICacheService
{
private readonly IMemoryCache _memoryCache;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private long _hits = 0;
private long _misses = 0;
private long _memoryUsage = 0;
public MemoryCacheService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public Task GetOrSetAsync(string key, Func> factory, MemoryCacheEntryOptions options = null)
{
return Task.Run(async () =>
{
// Try to get from cache first
if (_memoryCache.TryGetValue(key, out T value))
{
Interlocked.Increment(ref _hits);
return value;
}
Interlocked.Increment(ref _misses);
// Create new value
value = await factory();
if (value != null)
{
// Set cache options if not provided
options = options ?? new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
Size = 1024 // 1KB
};
// Set cache with lock
_lock.EnterWriteLock();
try
{
_memoryCache.Set(key, value, options);
UpdateMemoryUsage(options.Size.GetValueOrDefault(1024));
}
finally
{
_lock.ExitWriteLock();
}
}
return value;
});
}
public T Get(string key)
{
if (_memoryCache.TryGetValue(key, out T value))
{
Interlocked.Increment(ref _hits);
return value;
}
Interlocked.Increment(ref _misses);
return default(T);
}
public void Set(string key, T value, MemoryCacheEntryOptions options = null)
{
if (value == null)
return;
options = options ?? new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
Size = 1024 // 1KB
};
_lock.EnterWriteLock();
try
{
_memoryCache.Set(key, value, options);
UpdateMemoryUsage(options.Size.GetValueOrDefault(1024));
}
finally
{
_lock.ExitWriteLock();
}
}
public bool Remove(string key)
{
_lock.EnterWriteLock();
try
{
if (_memoryCache.TryGetValue(key, out object value))
{
_memoryCache.Remove(key);
UpdateMemoryUsage(-(GetEstimatedSize(value)));
return true;
}
return false;
}
finally
{
_lock.ExitWriteLock();
}
}
public bool Exists(string key)
{
return _memoryCache.TryGetValue(key, out _);
}
public void Clear()
{
_lock.EnterWriteLock();
try
{
// This is a simplified implementation
// In a real scenario, you might need a more sophisticated way to clear the cache
_memoryCache.Compact(1.0); // Remove all entries
_memoryUsage = 0;
Interlocked.Exchange(ref _hits, 0);
Interlocked.Exchange(ref _misses, 0);
}
finally
{
_lock.ExitWriteLock();
}
}
public CacheStatistics GetStatistics()
{
_lock.EnterReadLock();
try
{
var stats = new CacheStatistics
{
TotalItems = GetCacheSize(),
Hits = _hits,
Misses = _misses,
MemoryUsageBytes = _memoryUsage,
LastCleared = DateTime.Now,
ItemsByType = new Dictionary()
};
// Count items by type (simplified)
var deviceCacheCount = GetKeys("device:*").Count();
var productionCacheCount = GetKeys("production:*").Count();
var configCacheCount = GetKeys("config:*").Count();
var templateCacheCount = GetKeys("template:*").Count();
stats.ItemsByType["device"] = deviceCacheCount;
stats.ItemsByType["production"] = productionCacheCount;
stats.ItemsByType["config"] = configCacheCount;
stats.ItemsByType["template"] = templateCacheCount;
return stats;
}
finally
{
_lock.ExitReadLock();
}
}
public IEnumerable GetKeys(string pattern)
{
// This is a simplified implementation
// In a real scenario, you might need a more sophisticated key pattern matching
return _memoryCache.Keys
.Cast()
.Where(key => key.StartsWith(pattern.Replace("*", "")));
}
public bool Refresh(string key)
{
if (!_memoryCache.TryGetValue(key, out T value))
return false;
// Remove and re-add to refresh
_lock.EnterWriteLock();
try
{
_memoryCache.Remove(key);
_memoryCache.Set(key, value, new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
});
return true;
}
finally
{
_lock.ExitWriteLock();
}
}
#region Private Methods
private int GetCacheSize()
{
// This is an approximation
return _memoryCache.Count;
}
private void UpdateMemoryUsage(long delta)
{
Interlocked.Add(ref _memoryUsage, delta);
}
private int GetEstimatedSize(object obj)
{
// Simplified size estimation
if (obj == null) return 0;
var type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return System.Text.Json.JsonSerializer.Serialize(obj).Length;
}
// For complex objects, estimate based on type
return 1024; // Default 1KB for complex objects
}
#endregion
}
// Extension methods for common caching patterns
public static class CacheServiceExtensions
{
///
/// Cache device information
///
public static Task GetOrSetDeviceAsync(this ICacheService cache, int deviceId,
Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions();
if (expiration.HasValue)
{
options.SlidingExpiration = expiration.Value;
options.AbsoluteExpirationRelativeToNow = expiration.Value + TimeSpan.FromMinutes(30);
}
else
{
options.SlidingExpiration = TimeSpan.FromMinutes(30);
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2);
}
return cache.GetOrSetAsync($"device:{deviceId}", factory, options);
}
///
/// Cache device list
///
public static Task> GetOrSetAllDevicesAsync(this ICacheService cache,
Func>> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(15),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
};
return cache.GetOrSetAsync("devices:all", factory, options);
}
///
/// Cache device status
///
public static Task GetOrSetDeviceStatusAsync(this ICacheService cache, int deviceId,
Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(5),
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
};
return cache.GetOrSetAsync($"device:status:{deviceId}", factory, options);
}
///
/// Cache production records
///
public static Task> GetOrSetProductionRecordsAsync(this ICacheService cache,
int deviceId, DateTime date, Func>> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
};
return cache.GetOrSetAsync($"production:records:{deviceId}:{date:yyyy-MM-dd}", factory, options);
}
///
/// Cache production summary
///
public static Task GetOrSetProductionSummaryAsync(this ICacheService cache,
int deviceId, string programName, Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
};
return cache.GetOrSetAsync($"production:summary:{deviceId}:{programName}", factory, options);
}
///
/// Cache system configuration
///
public static Task GetOrSetSystemConfigurationAsync(this ICacheService cache,
Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromHours(1),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)
};
return cache.GetOrSetAsync("config:system", factory, options);
}
///
/// Cache template
///
public static Task GetOrSetTemplateAsync(this ICacheService cache,
int templateId, Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
};
return cache.GetOrSetAsync($"template:{templateId}", factory, options);
}
///
/// Cache template list
///
public static Task> GetOrSetAllTemplatesAsync(this ICacheService cache,
Func>> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
};
return cache.GetOrSetAsync("templates:all", factory, options);
}
///
/// Cache alert configuration
///
public static Task GetOrSetAlertConfigurationAsync(this ICacheService cache,
Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
};
return cache.GetOrSetAsync("config:alerts", factory, options);
}
///
/// Cache dashboard summary
///
public static Task GetOrSetDashboardSummaryAsync(this ICacheService cache,
DateTime date, Func> factory, TimeSpan? expiration = null)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
};
return cache.GetOrSetAsync($"dashboard:summary:{date:yyyy-MM-dd}", factory, options);
}
///
/// Invalidate device-related cache
///
public static void InvalidateDeviceCache(this ICacheService cache, int deviceId, params string[] additionalKeys)
{
var keys = new[]
{
$"device:{deviceId}",
$"device:status:{deviceId}",
$"production:records:{deviceId}",
$"production:summary:{deviceId}"
};
foreach (var key in keys.Concat(additionalKeys))
{
cache.Remove(key);
}
}
///
/// Invalidate production-related cache
///
public static void InvalidateProductionCache(this ICacheService cache, int deviceId, string programName, DateTime date)
{
cache.Remove($"production:records:{deviceId}:{date:yyyy-MM-dd}");
cache.Remove($"production:summary:{deviceId}:{programName}");
}
///
/// Invalidate template-related cache
///
public static void InvalidateTemplateCache(this ICacheService cache, int templateId)
{
cache.Remove($"template:{templateId}");
cache.Remove("templates:all");
}
///
/// Invalidate system configuration cache
///
public static void InvalidateSystemConfigCache(this ICacheService cache)
{
cache.Remove("config:system");
cache.Remove("config:alerts");
cache.Remove("dashboard:summary");
}
///
/// Invalidate dashboard cache
///
public static void InvalidateDashboardCache(this ICacheService cache, DateTime date)
{
cache.Remove($"dashboard:summary:{date:yyyy-MM-dd}");
}
///
/// Get or set with sliding expiration for frequently accessed data
///
public static Task GetOrSetWithSlidingExpiration(this ICacheService cache, string key,
Func> factory, TimeSpan slidingExpiration)
{
var options = new MemoryCacheEntryOptions
{
SlidingExpiration = slidingExpiration
};
return cache.GetOrSetAsync(key, factory, options);
}
///
/// Get or set with absolute expiration for time-sensitive data
///
public static Task GetOrSetWithAbsoluteExpiration(this ICacheService cache, string key,
Func> factory, TimeSpan absoluteExpiration)
{
var options = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = absoluteExpiration
};
return cache.GetOrSetAsync(key, factory, options);
}
///
/// Get or set with priority for important data
///
public static Task GetOrSetWithPriority(this ICacheService cache, string key,
Func> factory, CacheItemPriority priority)
{
var options = new MemoryCacheEntryOptions
{
Priority = priority
};
return cache.GetOrSetAsync(key, factory, options);
}
}
///
/// Cache service for distributed caching
///
public interface IDistributedCacheService : ICacheService
{
///
/// Get distributed lock
///
Task AcquireLockAsync(string key, TimeSpan timeout);
///
/// Refresh distributed cache
///
Task RefreshAsync(string key);
///
/// Get distributed cache statistics
///
Task GetDistributedStatisticsAsync();
}
public interface IDistributedLock : IDisposable
{
bool IsAcquired { get; }
void Release();
}
public class DistributedCacheStatistics : CacheStatistics
{
public int ConnectedNodes { get; set; }
public long NetworkCalls { get; set; }
public long SynchronizationErrors { get; set; }
}
///
/// Cache manager for managing multiple cache instances
///
public interface ICacheManager
{
ICacheService LocalCache { get; }
IDistributedCacheService DistributedCache { get; }
void ConfigureCacheSettings(CacheSettings settings);
void InitializeCaches();
}
public class CacheSettings
{
public bool EnableLocalCache { get; set; } = true;
public bool EnableDistributedCache { get; set; } = false;
public TimeSpan DefaultSlidingExpiration { get; set; } = TimeSpan.FromMinutes(30);
public TimeSpan DefaultAbsoluteExpiration { get; set; } = TimeSpan.FromHours(2);
public long MaxMemorySizeBytes { get; set; } = 1024 * 1024 * 100; // 100MB
public CacheEvictionPolicy EvictionPolicy { get; set; } = CacheEvictionPolicy.LRU;
public bool EnableCacheLogging { get; set; } = false;
public TimeSpan CacheRefreshInterval { get; set; } = TimeSpan.FromMinutes(5);
}
public enum CacheEvictionPolicy
{
LRU, // Least Recently Used
LFU, // Least Frequently Used
FIFO, // First In First Out
Random
}
public class CacheManager : ICacheManager
{
public ICacheService LocalCache { get; private set; }
public IDistributedCacheService DistributedCache { get; private set; }
public CacheManager(ICacheService localCache, IDistributedCacheService distributedCache)
{
LocalCache = localCache;
DistributedCache = distributedCache;
}
public void ConfigureCacheSettings(CacheSettings settings)
{
// Configure cache settings based on provided configuration
// This would involve setting up the cache instances with the specified settings
}
public void InitializeCaches()
{
// Initialize caches with default settings
ClearAllCaches();
}
public void ClearAllCaches()
{
LocalCache?.Clear();
DistributedCache?.Clear();
}
}
}