-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCacheManager.cs
More file actions
145 lines (129 loc) · 4.77 KB
/
Copy pathCacheManager.cs
File metadata and controls
145 lines (129 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Runtime.Caching;
namespace CacheCommander
{
/// <summary>
/// Manages a dedicated MemoryCache instance with configurable memory limits.
/// Replaces the use of MemoryCache.Default to prevent unbounded memory growth
/// and isolate this library's cache from other consumers in the same process.
///
/// To configure memory limits, add the following to App.Config:
/// <configSections>
/// <section name="CacheCommander.Settings" type="System.Configuration.NameValueSectionHandler" />
/// </configSections>
/// <CacheCommander.Settings>
/// <add key="CacheName" value="CacheCommanderCache" />
/// <add key="CacheMemoryLimitMb" value="50" />
/// </CacheCommander.Settings>
/// </summary>
internal static class CacheManager
{
private const string ConfigSectionName = "CacheCommander.Settings";
private const string CacheNameKey = "CacheName";
private const string MemoryLimitKey = "CacheMemoryLimitMb";
private const string DefaultCacheName = "CacheCommanderCache";
private const int DefaultMemoryLimitMb = 0; // default to 0 - uncapped/managed by system memory
private static MemoryCache _cache;
private static readonly object _lock = new object();
/// <summary>
/// Gets the dedicated MemoryCache instance, creating it on first access.
/// Thread-safe lazy initialization with double-checked locking.
/// </summary>
internal static MemoryCache Instance
{
get
{
if (_cache == null)
{
lock (_lock)
{
if (_cache == null)
{
_cache = CreateCache();
}
}
}
return _cache;
}
}
/// <summary>
/// Retrieves a cached item by key. Returns null if the key does not exist.
/// </summary>
internal static object Get(string key)
{
if (string.IsNullOrEmpty(key))
return null;
return Instance.Get(key);
}
/// <summary>
/// Stores an item in the cache with the specified absolute expiration.
/// </summary>
internal static void Set(string key, object value, DateTimeOffset absoluteExpiration)
{
if (string.IsNullOrEmpty(key) || value == null)
return;
Instance.Set(key, value, absoluteExpiration);
}
/// <summary>
/// Checks whether a cache entry exists for the given key.
/// </summary>
internal static bool Contains(string key)
{
if (string.IsNullOrEmpty(key))
return false;
// Use Get() instead of Contains() to avoid double-lookup overhead.
return Instance.Get(key) != null;
}
/// <summary>
/// Clears all entries from the cache. Useful for testing or runtime invalidation.
/// </summary>
internal static void Clear()
{
var keys = Instance.Select(entry => entry.Key).ToList();
foreach (var key in keys)
{
Instance.Remove(key);
}
}
private static MemoryCache CreateCache()
{
string cacheName = GetConfigValue(CacheNameKey, DefaultCacheName);
int memoryLimitMb = GetConfiguredMemoryLimit();
var cacheConfig = new NameValueCollection();
cacheConfig.Add("cacheMemoryLimitMegabytes", memoryLimitMb.ToString());
return new MemoryCache(cacheName, cacheConfig);
}
private static int GetConfiguredMemoryLimit()
{
string value = GetConfigValue(MemoryLimitKey, DefaultMemoryLimitMb.ToString());
if (int.TryParse(value, out int limit) && limit > 0)
{
return limit;
}
return DefaultMemoryLimitMb;
}
private static string GetConfigValue(string key, string defaultValue)
{
try
{
var config = ConfigurationManager.GetSection(ConfigSectionName);
if (config is NameValueCollection collection)
{
string value = collection[key];
if (!string.IsNullOrEmpty(value))
{
return value;
}
}
}
catch
{
// If config section is missing or unreadable, use defaults
}
return defaultValue;
}
}
}