2using System.Collections;
3using System.Collections.Generic;
14 public class Cache<KeyType, ValueType> :
ICache, IDictionary<KeyType, ValueType>
16 private readonly Guid
id = Guid.NewGuid();
17 private readonly Dictionary<KeyType, CacheItem<KeyType, ValueType>> valuesByKey =
new Dictionary<KeyType, CacheItem<KeyType, ValueType>>();
18 private readonly SortedDictionary<DateTime, KeyType> keysByLastUsage =
new SortedDictionary<DateTime, KeyType>();
19 private readonly SortedDictionary<DateTime, KeyType> keysByCreation =
new SortedDictionary<DateTime, KeyType>();
20 private readonly Random rnd =
new Random();
21 private readonly
object synchObject =
new object();
22 private readonly
int maxItems;
23 private readonly
bool standalone;
24 private TimeSpan maxTimeUsed;
25 private TimeSpan maxTimeUnused;
54 Caches.Register(this.
id,
this);
57 private void CreateTimerLocked()
59 if (this.maxTimeUsed < TimeSpan.MaxValue ||
this.maxTimeUnused < TimeSpan.MaxValue)
61 int Interval = Math.Min((
int)((this.maxTimeUnused.TotalMilliseconds / 2) + 0.5), 5000);
65 this.timer =
new Timer(this.TimerCallback,
null, Interval, Interval);
76 Caches.Unregister(this.
id);
86 private void TimerCallback(
object state)
88 LinkedList<CacheItem<KeyType, ValueType>> ToRemove1 =
null;
89 LinkedList<CacheItem<KeyType, ValueType>> ToRemove2 =
null;
90 DateTime Now = DateTime.Now;
95 lock (this.synchObject)
97 if (this.maxTimeUnused < TimeSpan.MaxValue)
99 Limit = Now - this.maxTimeUnused;
101 foreach (KeyValuePair<DateTime, KeyType> P
in this.keysByLastUsage)
106 if (ToRemove1 is
null)
107 ToRemove1 =
new LinkedList<CacheItem<KeyType, ValueType>>();
109 ToRemove1.AddLast(this.valuesByKey[P.Value]);
112 if (!(ToRemove1 is
null))
114 foreach (CacheItem<KeyType, ValueType> Item
in ToRemove1)
116 this.valuesByKey.Remove(Item.Key);
117 this.keysByCreation.Remove(Item.Created);
118 this.keysByLastUsage.Remove(Item.LastUsed);
121 if (this.valuesByKey.Count == 0)
123 this.timer?.Dispose();
129 if (this.maxTimeUsed < TimeSpan.MaxValue)
131 Limit = Now - this.maxTimeUsed;
133 foreach (KeyValuePair<DateTime, KeyType> P
in this.keysByCreation)
138 if (ToRemove2 is
null)
139 ToRemove2 =
new LinkedList<CacheItem<KeyType, ValueType>>();
141 ToRemove2.AddLast(this.valuesByKey[P.Value]);
144 if (!(ToRemove2 is
null))
146 foreach (CacheItem<KeyType, ValueType> Item
in ToRemove2)
148 this.valuesByKey.Remove(Item.Key);
149 this.keysByCreation.Remove(Item.Created);
150 this.keysByLastUsage.Remove(Item.LastUsed);
153 if (this.valuesByKey.Count == 0)
155 this.timer?.Dispose();
162 if (!(ToRemove1 is
null))
165 if (!(ToRemove2 is
null))
184 get => this.maxTimeUsed;
185 set => this.maxTimeUsed = value;
193 get => this.maxTimeUnused;
194 set => this.maxTimeUnused = value;
205 lock (this.synchObject)
207 if (this.valuesByKey.TryGetValue(Key, out CacheItem<KeyType, ValueType> Item))
211 this.keysByLastUsage.Remove(Item.LastUsed);
212 Item.LastUsed = this.GetLastUsageTimeLocked();
213 this.keysByLastUsage[Item.LastUsed] = Key;
232 lock (this.synchObject)
234 return this.valuesByKey.Count;
262 lock (this.synchObject)
264 Result =
new KeyType[this.valuesByKey.Count];
265 this.valuesByKey.Keys.CopyTo(Result, 0);
280 lock (this.synchObject)
282 Result =
new ValueType[this.valuesByKey.Count];
284 foreach (CacheItem<KeyType, ValueType> Rec
in this.valuesByKey.Values)
285 Result[i++] = Rec.Value;
301 private DateTime GetLastUsageTimeLocked()
303 DateTime TP = DateTime.Now;
305 while (this.keysByLastUsage.ContainsKey(TP))
306 TP = TP.AddTicks(this.rnd.Next(1, 10));
317 public ValueType
this[KeyType Key]
324 throw new ArgumentException(
"Value not found.", nameof(Key));
329 this.
Add(Key, value);
338 public void Add(KeyType Key, ValueType Value)
340 CacheItem<KeyType, ValueType> Prev;
341 CacheItem<KeyType, ValueType> Item;
344 lock (this.synchObject)
346 if (this.valuesByKey.TryGetValue(Key, out Prev))
348 this.valuesByKey.Remove(Key);
349 this.keysByCreation.Remove(Prev.Created);
350 this.keysByLastUsage.Remove(Prev.LastUsed);
357 if (this.valuesByKey.Count >=
this.maxItems)
359 KeyType OldKey =
default;
362 foreach (KeyType Key2
in this.keysByLastUsage.Values)
371 Prev = this.valuesByKey[OldKey];
373 this.valuesByKey.Remove(OldKey);
374 this.keysByCreation.Remove(Prev.Created);
375 this.keysByLastUsage.Remove(Prev.LastUsed);
384 DateTime TP = DateTime.Now;
386 while (this.keysByCreation.ContainsKey(TP) ||
this.keysByLastUsage.ContainsKey(TP))
387 TP = TP.AddTicks(this.rnd.Next(1, 10));
389 Item =
new CacheItem<KeyType, ValueType>(Key, Value, TP);
391 this.valuesByKey[Key] = Item;
392 this.keysByCreation[TP] = Key;
393 this.keysByLastUsage[TP] = Key;
395 if (this.timer is
null)
396 this.CreateTimerLocked();
400 this.OnRemoved(Key, Prev.Value, Reason);
403 private async
void OnRemoved(KeyType Key, ValueType Value,
RemovedReason Reason)
420 private async
void OnRemoved(IEnumerable<CacheItem<KeyType, ValueType>> Items,
RemovedReason Reason)
427 foreach (CacheItem<KeyType, ValueType> Item
in Items)
431 await h(
this,
new CacheItemEventArgs<KeyType, ValueType>(Item.Key, Item.Value, Reason));
453 CacheItem<KeyType, ValueType> Item;
455 lock (this.synchObject)
457 if (!this.valuesByKey.TryGetValue(Key, out Item))
460 this.valuesByKey.Remove(Item.Key);
461 this.keysByCreation.Remove(Item.Created);
462 this.keysByLastUsage.Remove(Item.LastUsed);
464 if (this.valuesByKey.Count == 0)
466 this.timer?.Dispose();
486 CacheItem<KeyType, ValueType>[]
Values;
488 lock (this.synchObject)
490 Values =
new CacheItem<KeyType, ValueType>[this.valuesByKey.Count];
491 this.valuesByKey.Values.CopyTo(
Values, 0);
492 this.valuesByKey.Clear();
493 this.keysByLastUsage.Clear();
494 this.keysByCreation.Clear();
496 this.timer?.Dispose();
507 public void Add(KeyValuePair<KeyType, ValueType> item)
509 this.
Add(item.Key, item.Value);
517 public bool Contains(KeyValuePair<KeyType, ValueType> item)
519 return this.
TryGetValue(item.Key, out ValueType Value) && Value.Equals(item.Value);
527 public void CopyTo(KeyValuePair<KeyType, ValueType>[] array,
int arrayIndex)
529 lock (this.synchObject)
531 foreach (CacheItem<KeyType, ValueType> Item
in this.valuesByKey.Values)
532 array[arrayIndex++] =
new KeyValuePair<KeyType, ValueType>(Item.Key, Item.Value);
540 public KeyValuePair<KeyType, ValueType>[]
ToArray()
542 KeyValuePair<KeyType, ValueType>[] Result;
545 lock (this.synchObject)
547 Result =
new KeyValuePair<KeyType, ValueType>[this.valuesByKey.Count];
549 foreach (CacheItem<KeyType, ValueType> Item
in this.valuesByKey.Values)
550 Result[i++] =
new KeyValuePair<KeyType, ValueType>(Item.Key, Item.Value);
561 public bool Remove(KeyValuePair<KeyType, ValueType> item)
563 if (this.
TryGetValue(item.Key, out ValueType Value) && Value.Equals(item.Value))
564 return this.
Remove(item.Key);
575 IEnumerable<KeyValuePair<KeyType, ValueType>> Array = this.
ToArray();
576 return Array.GetEnumerator();
583 IEnumerator IEnumerable.GetEnumerator()
585 return this.
ToArray().GetEnumerator();
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Implements an in-memory cache.
ValueType[] GetValues()
Gets all available values in the cache.
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
KeyValuePair< KeyType, ValueType >[] ToArray()
Returns the contents of the cache as an array.
Cache(int MaxItems, TimeSpan MaxTimeUsed, TimeSpan MaxTimeUnused)
Implements an in-memory cache.
void Dispose()
IDisposable.Dispose
bool Standalone
If cache is a standalone cache, or if it can be managed collectively with other caches.
int Count
Number of items in cache
bool Remove(KeyValuePair< KeyType, ValueType > item)
Removes an item from the cache.
ICollection< ValueType > Values
Values in cache.
bool IsReadOnly
If the dictionary is read-only.
TimeSpan MaxTimeUsed
Maximum time to keep items that are being used.
bool Remove(KeyType Key)
Removes an item from the cache.
CacheItemEventHandler< KeyType, ValueType > Removed
Event raised when an item has been removed from the cache.
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
int MaxItems
Maximum number of items in cache.
void CopyTo(KeyValuePair< KeyType, ValueType >[] array, int arrayIndex)
Copies all items in the cache to an array.
void Add(KeyValuePair< KeyType, ValueType > item)
Adds an item to the cache.
IEnumerator< KeyValuePair< KeyType, ValueType > > GetEnumerator()
Gets an enumerator of contents in the cache.
KeyType[] GetKeys()
Gets all available keys in the cache.
ICollection< KeyType > Keys
Keys in cache.
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
void Clear()
Clears the cache.
Cache(int MaxItems, TimeSpan MaxTimeUsed, TimeSpan MaxTimeUnused, bool Standalone)
Implements an in-memory cache.
bool Contains(KeyValuePair< KeyType, ValueType > item)
Checks if an item (key and value) exists in the cache.
TimeSpan MaxTimeUnused
Maximum time to keep items that are not being used.
Event arguments for cache item removal events.
Repository of all active caches.
delegate Task CacheItemEventHandler< KeyType, ValueType >(object Sender, CacheItemEventArgs< KeyType, ValueType > e)
Delegate for cache item removal event handlers.
RemovedReason
Reason for removing the item.