heinrich23

C# easy Singleton Cache Pattern

Apr 27th, 2025 (edited)
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 28.43 KB | Source Code | 0 0
  1. using Newtonsoft.Json;
  2. using StackExchange.Redis;
  3. using System.Configuration;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Collections.Concurrent;
  10.  
  11. namespace ConsoleAppCacheTest
  12. {
  13.  
  14.     /// <summary>
  15.     /// CacheTypVal any cached value.
  16.     /// Use default empty ctor <see cref="CacheTypVal()"/> and
  17.     /// <see cref="SetValue{T}(T)"/> to set the cached value;
  18.     /// setting cache value via <see cref="CacheTypVal(object, Type)"/> ctor is obsolete.
  19.     /// Use <see cref="GetValue{T}"/> to get the cached value
  20.     /// </summary>
  21.     public class CacheTypVal
  22.     {
  23.  
  24.         protected internal object? _Value { get; set; }
  25.         protected internal Type? _Type { get; set; }
  26.  
  27.         /// <summary>
  28.         /// Empty default ctor
  29.         /// </summary>
  30.         public CacheTypVal()
  31.         {
  32.             _Type = null;
  33.             _Value = null;
  34.         }
  35.  
  36.         /// <summary>
  37.         /// Obsolete ctor, please use default empty ctor <see cref="CacheTypVal()"/>
  38.         /// and then <see cref="SetValue{T}(T)"/> to set a cached value instead.
  39.         /// </summary>
  40.         /// <param name="ovalue"><see cref="object" /> ovalue</param>
  41.         /// <param name="atype"><see cref="Type"/> atype</param>
  42.         [Obsolete("Don't use ctor CacheTypeValue(object, Type) to set a cache value, use SetValue<T>(T tvalue) instead.", false)]
  43.         public CacheTypVal(object ovalue, Type atype)
  44.         {
  45.             _Type = atype;
  46.             _Value = ovalue;
  47.         }
  48.  
  49.         /// <summary>
  50.         /// gets the <see cref="Type"/> of generic cached value
  51.         /// </summary>
  52.         /// <returns><see cref="Type"/> of generic value or null if cached value is <see cref="null"/></returns>
  53.         public Type? GetType()
  54.         {
  55.             return _Type;
  56.         }
  57.  
  58.         /// <summary>
  59.         /// Get a value from cache
  60.         /// </summary>
  61.         /// <typeparam name="T">generic type of value passed by typeparameter</typeparam>
  62.         /// <returns>generic T value</returns>
  63.         /// <exception cref="InvalidOperationException">thrown, when cached value isn't of typeof(T)</exception>
  64.         public T? GetValue<T>()
  65.         {
  66.             T? tvalue;
  67.             if (typeof(T) == _Type)
  68.                 tvalue = (T?)_Value;
  69.             else
  70.                 throw new InvalidOperationException($"typeof(T) = {typeof(T)} while _type = {_Type}");
  71.  
  72.             return tvalue ?? default(T);
  73.         }
  74.  
  75.         /// <summary>
  76.         /// Sets a generic cached value
  77.         /// </summary>
  78.         /// <typeparam name="T">generic type of value passed by typeparameter</typeparam>
  79.         /// <param name="tvalue">generic value to set cached</param>
  80.         public void SetValue<T>(T tvalue)
  81.         {
  82.             _Type = typeof(T);
  83.             _Value = (object)tvalue;
  84.         }
  85.  
  86.     }
  87.  
  88.  
  89.     /// <summary>
  90.     /// MemCache an application cache implemented saved in memory only at runtime
  91.     /// derive from <see cref="MemCache"/> and implement your own cache by implementing a new variant
  92.     /// </summary>
  93.     public abstract class MemCache
  94.     {
  95.  
  96.         public const string APP_CONCURRENT_DICT = "APP_CONCURRENT_DICT";
  97.  
  98.         protected internal static readonly Lazy<MemCache> _instance;
  99.         public static MemCache CacheDict => _instance.Value;
  100.  
  101.         /// <summary>
  102.         /// private <see cref="ConcurrentDictionary{string, CacheTypeValue}"/>
  103.         /// </summary>
  104.         protected internal static ConcurrentDictionary<string, CacheTypVal> _appCache = new ConcurrentDictionary<string, CacheTypVal>();
  105.  
  106.         /// <summary>
  107.         /// public property get accessor for <see cref="_appCache"/> stored in <see cref="AppDomain.CurrentDomain"/>
  108.         /// </summary>
  109.         protected virtual ConcurrentDictionary<string, CacheTypVal> AppCache
  110.         {
  111.             get
  112.             {
  113.                 // _appCache =  (ConcurrentDictionary<string, CacheTypVal>) get it where to get it
  114.                 if (_appCache == null)
  115.                 {
  116.                     _appCache = new ConcurrentDictionary<string, CacheTypVal>();
  117.                     // where to set it _appCache
  118.                 }
  119.                 return _appCache;
  120.             }
  121.             set
  122.             {
  123.                 if (value != null && value.Count > 0)
  124.                     _appCache = value;
  125.                 // if (_appCache != null && _appCache.Count > 0)
  126.                 //      set it where to set it _appCache
  127.             }
  128.         }
  129.  
  130.         /// <summary>
  131.         /// Get all keys from <see cref="AppCache"/> which is implemented as a <see cref="ConcurrentDictionary{string, CacheTypVal}"/>
  132.         /// </summary>
  133.         public virtual string[] AllKeys { get => AppCache.Keys.ToArray(); }
  134.  
  135.         /// <summary>
  136.         /// static ctor
  137.         /// </summary>
  138.         static MemCache()
  139.         {
  140.             if (ConfigurationManager.AppSettings["PersistMsgIn"] != null)
  141.             {
  142.                 string persistMsgIn = (string)ConfigurationManager.AppSettings["PersistMsgIn"];
  143.                 switch (persistMsgIn)
  144.                 {
  145.                     case "ApplicationState":
  146.                     case "JsonFile":
  147.                         _instance = new Lazy<MemCache>(() => new JsonCache());
  148.                         break;
  149.                     case "AmazonElasticCache":
  150.                         // TODO: Redis
  151.                         _instance = new Lazy<MemCache>(() => new RedisCache());
  152.                         break;
  153.                     case "AppDomainData":
  154.                         _instance = new Lazy<MemCache>(() => new AppDomainCache());
  155.                         break;
  156.                     default:
  157.                         _instance = new Lazy<MemCache>(() => new JsonCache());
  158.                         break;
  159.                 }
  160.             }
  161.             else
  162.             {
  163.                 // _instance = new Lazy<MemCache>(() => new RedisCache());
  164.                 _instance = new Lazy<MemCache>(() => new JsonCache());
  165.             }
  166.         }
  167.  
  168.  
  169.         /// <summary>
  170.         /// Static constructor
  171.         /// </summary>
  172.         public MemCache()
  173.         {
  174.             _appCache = new ConcurrentDictionary<string, CacheTypVal>();
  175.         }
  176.  
  177.         /// <summary>
  178.         /// Gets a value from <see cref="ConcurrentDictionary<string, CacheTypVal>"/> stored <see cref="System.AppDomain.CurrentDomain"/>
  179.         /// </summary>
  180.         /// <typeparam name="T">generic type of cached value</typeparam>
  181.         /// <param name="ckey">cache key</param>
  182.         /// <returns>generic cached value stored at key</returns>
  183.         public virtual T GetValue<T>(string ckey)
  184.         {
  185.             if (AppCache.ContainsKey(ckey) && AppCache.TryGetValue(ckey, out var cvalue))
  186.                 return cvalue.GetValue<T>();
  187.  
  188.             return default(T);
  189.         }
  190.  
  191.         /// <summary>
  192.         /// Sets a generic value to <see cref="ConcurrentDictionary<string, CacheTypVal>"/> stored <see cref="System.AppDomain.CurrentDomain"/>
  193.         /// </summary>
  194.         /// <typeparam name="T">generic type of cached value</typeparam>
  195.         /// <param name="ckey">cache key</param>
  196.         /// <param name="cvalue">generic value to stored at key in cache</param>
  197.         /// <returns>true, if add or updated succeeded, otherwise false</returns>
  198.         public virtual bool SetValue<T>(string ckey, T tvalue)
  199.         {
  200.             bool addedOrUpdated = false;
  201.  
  202.             if (string.IsNullOrEmpty(ckey) || tvalue == null)
  203.                 return false;
  204.  
  205.             CacheTypVal cvalue = new CacheTypVal();
  206.             cvalue.SetValue<T>(tvalue);
  207.  
  208.             if (!AppCache.ContainsKey(ckey))
  209.                 addedOrUpdated = AppCache.TryAdd(ckey, cvalue);
  210.             else if (AppCache.TryGetValue(ckey, out CacheTypVal oldValue))
  211.                 addedOrUpdated = _appCache.TryUpdate(ckey, cvalue, oldValue);
  212.  
  213.             // MAYBE SHORTER BUT NOBODY CAN QUICK READ AND UNDERSTAND THIS
  214.             // addedOrUpdated = (!AppCache.ContainsKey(ckey)) ? AppCache.TryAdd(ckey, cvalue) :
  215.             //    (AppCache.TryGetValue(ckey, out CacheTypVal oldValue)) ? _appCache.TryUpdate(ckey, cvalue, oldValue) : false;
  216.  
  217.             if (addedOrUpdated)
  218.                 AppCache = _appCache;  // saves the modified ConcurrentDictionary{string, CacheTypVal} back to AppDomain
  219.  
  220.             return addedOrUpdated;
  221.         }
  222.  
  223.         /// <summary>
  224.         /// Looks, if  <see cref="ConcurrentDictionary{string, CacheTypVal}"/>  contains the key
  225.         /// </summary>
  226.         /// <param name="ckey">lookup key</param>
  227.         /// <returns>true, if ckey is not null or empty and <see cref="AppCache"/> contains ckey, otherwise false</returns>
  228.         public virtual bool ContainsKey(string ckey)
  229.         {
  230.             return (!string.IsNullOrEmpty(ckey) && AppCache.ContainsKey(ckey));
  231.         }
  232.  
  233.         /// <summary>
  234.         /// RemoveKey removes a key value pair from <see cref="AppCache"/>
  235.         /// </summary>
  236.         /// <param name="ckey">key to remove</param>
  237.         /// <returns>true, if key value pair was successfully removed or <see cref="AppCache"/> doesn't contain anymore ckey;
  238.         /// false if ckey is <see cref="null"/> or <see cref="string.Empty"/> or removing ckey from <see cref="ConcurrentDictionary{string, CacheTypVal}"/> failed.</returns>
  239.         public virtual bool RemoveKey(string ckey)
  240.         {
  241.             if (string.IsNullOrEmpty(ckey))
  242.                 return false;
  243.  
  244.             if (!AppCache.ContainsKey(ckey))
  245.                 return true;
  246.  
  247.             if (!AppCache.TryRemove(ckey, out CacheTypVal cvalue))
  248.                 return false;
  249.  
  250.             AppCache = _appCache; // saves the modified ConcurrentDictionary{string, CacheTypVal} back to AppDomain
  251.  
  252.             return true;
  253.         }
  254.  
  255.     }
  256.  
  257.  
  258.     /// <summary>
  259.     /// JsonCache an application cache implemented with <see cref="ConcurrentDictionary{string, CacheTypVal}"/> serialized with json    
  260.     /// </summary>
  261.     public class JsonCache : MemCache
  262.     {
  263.        
  264.         //protected internal static readonly Lazy<MemCache> _instance = new Lazy<MemCache>(() => new JsonCache());
  265.         //public static MemCache CacheDict => _instance.Value;
  266.  
  267.         const int INIT_SEM_COUNT = 1;
  268.         const int MAX_SEM_COUNT = 1;
  269.         const string JSON_APPCACHE_FILE = "AppCache.json";
  270.         readonly static string JsonFullDirPath = Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "TEMP");
  271.         readonly static string JsonFullFilePath = Path.Combine(JsonFullDirPath, JSON_APPCACHE_FILE);
  272.  
  273.         protected static readonly Lock _lock = new Lock();
  274.         // protected static Mutex mutex = new Mutex(true, JSON_APPCACHE_FILE);
  275.         protected static SemaphoreSlim ReadWriteSemaphore = new SemaphoreSlim(INIT_SEM_COUNT, MAX_SEM_COUNT);
  276.  
  277.         protected static JsonSerializerSettings JsonSettings = new JsonSerializerSettings()
  278.         {
  279.             Formatting = Formatting.Indented,
  280.             MaxDepth = 16,
  281.             NullValueHandling = NullValueHandling.Include,
  282.             MissingMemberHandling = MissingMemberHandling.Ignore,
  283.             ObjectCreationHandling = ObjectCreationHandling.Auto,
  284.             DateFormatHandling = DateFormatHandling.IsoDateFormat,
  285.             DateParseHandling = DateParseHandling.DateTime,
  286.             PreserveReferencesHandling = PreserveReferencesHandling.All,
  287.             ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
  288.         };
  289.  
  290.         /// <summary>
  291.         /// public property get accessor for <see cref="_appCache"/> stored in <see cref="AppDomain.CurrentDomain"/>
  292.         /// </summary>
  293.         protected override ConcurrentDictionary<string, CacheTypVal> AppCache
  294.         {
  295.             get
  296.             {
  297.                 int semCnt = 0;
  298.                 try
  299.                 {
  300.                     ReadWriteSemaphore.Wait(125);
  301.                     // if (mutex.WaitOne(250, false))
  302.                     if (_appCache == null || _appCache.Count == 0)
  303.                     {
  304.                         lock (_lock)
  305.                         {                            
  306.                             if (!Directory.Exists(JsonFullDirPath))
  307.                                 Directory.CreateDirectory(JsonFullDirPath);
  308.  
  309.                             string jsonSerializedAppDict = (System.IO.File.Exists(JsonFullFilePath)) ? System.IO.File.ReadAllText(JsonFullFilePath) : "";
  310.                             if (!string.IsNullOrEmpty(jsonSerializedAppDict))
  311.                                 _appCache = (ConcurrentDictionary<string, CacheTypVal>)JsonConvert.DeserializeObject<ConcurrentDictionary<string, CacheTypVal>>(jsonSerializedAppDict);
  312.                         }
  313.                         // semCnt = ReadWriteSemaphore.Release();
  314.                     }
  315.  
  316.                     if (_appCache == null || _appCache.Count == 0)                    
  317.                     {
  318.                         lock (_lock)
  319.                         {
  320.                             _appCache = new ConcurrentDictionary<string, CacheTypVal>();
  321.                             // set it, where to set it _appCache
  322.                             string jsonDeserializedAppDict = JsonConvert.SerializeObject(_appCache, Formatting.Indented, JsonSettings);
  323.                             System.IO.File.WriteAllText(JsonFullFilePath, jsonDeserializedAppDict, Encoding.UTF8);
  324.                         }
  325.                     }
  326.                 }
  327.                 catch (Exception exGetRead)
  328.                 {
  329.                     Console.WriteLine($"Exception {exGetRead.GetType()}: {exGetRead.Message} \r\n\t{exGetRead}");
  330.                 }
  331.                 finally
  332.                 {
  333.                     if (ReadWriteSemaphore.CurrentCount > 0)
  334.                         semCnt = ReadWriteSemaphore.Release();
  335.                     // mutex.ReleaseMutex();
  336.                 }
  337.                 return _appCache;
  338.             }
  339.             set
  340.             {
  341.                 int semCnt = 0;
  342.                 try
  343.                 {
  344.                     semCnt = ReadWriteSemaphore.CurrentCount;
  345.                     ReadWriteSemaphore.Wait(125);
  346.  
  347.                     string jsonDeserializedAppDict = "";
  348.                     if (value != null && value.Count > 0)
  349.                     {
  350.                         // if (mutex.WaitOne(250, false))
  351.                         lock (_lock)
  352.                         {
  353.                             _appCache = value;
  354.  
  355.                             // set it, where to set it _appCache
  356.                             jsonDeserializedAppDict = JsonConvert.SerializeObject(_appCache, Formatting.Indented, JsonSettings);
  357.                             System.IO.File.WriteAllText(JsonFullFilePath, jsonDeserializedAppDict, Encoding.UTF8);
  358.                         }
  359.                     }
  360.                 }
  361.                 catch (Exception exSetWrite)
  362.                 {
  363.                     Console.WriteLine($"Exception {exSetWrite.GetType()}: {exSetWrite.Message} \r\n\t{exSetWrite}");
  364.                 }
  365.                 finally
  366.                 {                    
  367.                     if (ReadWriteSemaphore.CurrentCount > 0)
  368.                         semCnt = ReadWriteSemaphore.Release();
  369.                 }
  370.             }
  371.         }
  372.     }
  373.  
  374.  
  375.     /// <summary>
  376.     /// JsonCache an application cache implemented with a <see cref="ConcurrentDictionary{string, CacheTypVal}"/>
  377.     /// </summary>
  378.     public class AppDomainCache : MemCache
  379.     {
  380.  
  381.         /// <summary>
  382.         /// public property get accessor for <see cref="_appCache"/> stored in <see cref="AppDomain.CurrentDomain"/>
  383.         /// </summary>
  384.         protected override ConcurrentDictionary<string, CacheTypVal> AppCache
  385.         {
  386.             get
  387.             {
  388.                 _appCache = (ConcurrentDictionary<string, CacheTypVal>)AppDomain.CurrentDomain.GetData(APP_CONCURRENT_DICT);
  389.                 if (_appCache == null)
  390.                 {
  391.                     _appCache = new ConcurrentDictionary<string, CacheTypVal>();
  392.                     AppDomain.CurrentDomain.SetData(APP_CONCURRENT_DICT, _appCache);
  393.                 }
  394.  
  395.                 return _appCache;
  396.             }
  397.             set
  398.             {
  399.                 if (value != null && value.Count > 0)
  400.                 {
  401.                     _appCache = value;
  402.                     AppDomain.CurrentDomain.SetData(APP_CONCURRENT_DICT, _appCache);
  403.                 }
  404.             }
  405.         }
  406.  
  407.         public AppDomainCache()
  408.         {
  409.             if (AppCache == null) ;
  410.         }
  411.     }
  412.  
  413.  
  414.     /// <summary>
  415.     /// RedisCache AWS elastic valkey cache singelton connector
  416.     /// </summary>
  417.     public class RedisCache : MemCache
  418.     {
  419.  
  420.         const string VALKEY_CACHE_HOST_PORT = "cqrcachecqrxseu-53g0xw.serverless.eus2.cache.amazonaws.com:6379";
  421.         const string VALKEY_CACHE_APP_KEY = "RedisValkeyCache";
  422.         const string ALL_KEYS = "AllKeys";
  423.  
  424.         private static readonly object _lock = new object();
  425.  
  426.         ConnectionMultiplexer connMux;
  427.         ConfigurationOptions options;
  428.         string endpoint = "cqrcachecqrxseu-53g0xw.serverless.eus2.cache.amazonaws.com:6379";
  429.         StackExchange.Redis.IDatabase db;
  430.  
  431.         public static MemCache ValKey => _instance.Value;
  432.  
  433.         private static HashSet<string> _allKeys = new HashSet<string>();
  434.         public override string[] AllKeys { get => GetAllKeys().ToArray(); }
  435.  
  436.         public static string EndPoint
  437.         {
  438.             get
  439.             {
  440.                 ((RedisCache)(_instance.Value)).endpoint = VALKEY_CACHE_HOST_PORT; // v              
  441.                 if (ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings[VALKEY_CACHE_APP_KEY] != null)
  442.                     ((RedisCache)(_instance.Value)).endpoint = (string)ConfigurationManager.AppSettings[VALKEY_CACHE_APP_KEY];
  443.                 return ((RedisCache)(_instance.Value)).endpoint;
  444.             }
  445.         }
  446.  
  447.         public static StackExchange.Redis.IDatabase Db
  448.         {
  449.             get
  450.             {
  451.                 if (((RedisCache)(_instance.Value)).db == null)
  452.                     ((RedisCache)(_instance.Value)).db = ConnMux.GetDatabase();
  453.  
  454.                 return ((RedisCache)(_instance.Value)).db;
  455.             }
  456.         }
  457.  
  458.         public static StackExchange.Redis.ConnectionMultiplexer ConnMux
  459.         {
  460.             get
  461.             {
  462.                 if (((RedisCache)(_instance.Value)).connMux == null)
  463.                 {
  464.                     if (((RedisCache)(_instance.Value)).options == null)
  465.                         ((RedisCache)(_instance.Value)).options = new ConfigurationOptions
  466.                         {
  467.                             EndPoints = { EndPoint },
  468.                             Ssl = true
  469.                         };
  470.                     ((RedisCache)(_instance.Value)).connMux = ConnectionMultiplexer.Connect(((RedisCache)(_instance.Value)).options);
  471.                 }
  472.                 return ((RedisCache)(_instance.Value)).connMux;
  473.             }
  474.         }
  475.  
  476.  
  477.         /// <summary>
  478.         /// default parameterless constructor for RedisCacheValKey cache singleton
  479.         /// </summary>
  480.         public RedisCache()
  481.         {
  482.             endpoint = VALKEY_CACHE_HOST_PORT; // "cqrcachecqrxseu-53g0xw.serverless.eus2.cache.amazonaws.com:6379";
  483.             if (ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings[VALKEY_CACHE_APP_KEY] != null)
  484.                 endpoint = (string)ConfigurationManager.AppSettings[VALKEY_CACHE_APP_KEY];
  485.             options = new ConfigurationOptions
  486.             {
  487.                 EndPoints = { endpoint },
  488.                 Ssl = true
  489.             };
  490.             if (connMux == null)
  491.                 connMux = ConnectionMultiplexer.Connect(options);
  492.             if (db == null)
  493.                 db = connMux.GetDatabase();
  494.         }
  495.  
  496.  
  497.         /// <summary>
  498.         /// GetString gets a string value by RedisCache key
  499.         /// </summary>
  500.         /// <param name="redIsKey">key</param>
  501.         /// <param name="flags"><see cref="CommandFlags"/></param>
  502.         /// <returns>(<see cref="string"/>) value for key redIsKey</returns>
  503.         public string GetString(string redIsKey, CommandFlags flags = CommandFlags.None)
  504.         {
  505.             string redIsString = Db.StringGet(redIsKey, flags);
  506.             return redIsString;
  507.         }
  508.  
  509.         /// <summary>
  510.         /// SetString set key with string value
  511.         /// </summary>
  512.         /// <param name="redIsKey">key for string/param>
  513.         /// <param name="redIsString"></param>
  514.         /// <param name="expiry"></param>
  515.         /// <param name="keepTtl"></param>
  516.         /// <param name="when"></param>
  517.         /// <param name="flags"></param>
  518.         public bool SetString(string redIsKey, string redIsString, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None)
  519.         {
  520.             bool success = false;
  521.             lock (_lock)
  522.             {
  523.                 var allRedIsKeys = GetAllKeys();
  524.                 success = Db.StringSet(redIsKey, redIsString, expiry, when, flags);
  525.  
  526.                 if (success && !allRedIsKeys.Contains(redIsKey))
  527.                 {
  528.                     allRedIsKeys.Add(redIsKey);
  529.                     string jsonVal = JsonConvert.SerializeObject(AllKeys);
  530.                     success = Db.StringSet(ALL_KEYS, jsonVal, null, keepTtl, When.Always, CommandFlags.None);
  531.                     _allKeys = allRedIsKeys;
  532.                 }
  533.             }
  534.  
  535.             return success;
  536.         }
  537.  
  538.         /// <summary>
  539.         /// SetValue sets value to cache
  540.         /// </summary>
  541.         /// <typeparam name="T">typeparameter</typeparam>
  542.         /// <param name="ckey">key to set</param>
  543.         /// <param name="tvalue">generic value</param>
  544.         /// <returns>success on true</returns>
  545.         public override bool SetValue<T>(string ckey, T tvalue)
  546.         {
  547.             TimeSpan? expiry = null;
  548.             bool keepTtl = false;
  549.             When when = When.Always;
  550.             CommandFlags flags = CommandFlags.None;
  551.             string jsonVal = JsonConvert.SerializeObject(tvalue);
  552.             bool success = SetString(ckey, jsonVal, expiry, keepTtl, when, flags);
  553.  
  554.             return success;
  555.         }
  556.  
  557.         /// <summary>
  558.         /// gets a generic class type T from redis cache with key
  559.         /// </summary>
  560.         /// <typeparam name="T"></typeparam>
  561.         /// <param name="ckey">rediskey</param>
  562.         /// <returns>T value/returns>
  563.         public override T GetValue<T>(string ckey)
  564.         {
  565.             CommandFlags flags = CommandFlags.None;
  566.             string jsonVal = Db.StringGet(ckey, flags);
  567.             T tval = default(T);
  568.             if (jsonVal != null)
  569.             {
  570.                 tval = JsonConvert.DeserializeObject<T>(jsonVal);
  571.             }
  572.  
  573.             return tval;
  574.         }
  575.  
  576.         /// <summary>
  577.         /// DeleteKey delete entry referenced at key
  578.         /// </summary>
  579.         /// <param name="redIsKey">key</param>
  580.         /// <param name="flags"><see cref="CommandFlags.FireAndForget"/> as default</param>
  581.         public override bool RemoveKey(string redIsKey)
  582.         {
  583.             CommandFlags flags = CommandFlags.FireAndForget;
  584.             lock (_lock)
  585.             {
  586.                 var allRedIsKeys = GetAllKeys();
  587.                 if (allRedIsKeys.Contains(redIsKey))
  588.                 {
  589.                     allRedIsKeys.Remove(redIsKey);
  590.                     string jsonVal = JsonConvert.SerializeObject(allRedIsKeys.ToArray());
  591.                     Db.StringSet("AllKeys", jsonVal, null, false, When.Always, flags);
  592.                     _allKeys = allRedIsKeys;
  593.                 }
  594.                 try
  595.                 {
  596.                     TimeSpan span = new TimeSpan(0, 0, 1);
  597.                     Db.StringGetDelete(redIsKey, flags);
  598.                 }
  599.                 catch (Exception ex)
  600.                 {
  601.                     Console.Error.WriteLine($"Exception {ex.GetType()}: {ex.Message}\r\n\t{ex}");
  602.                     return false;
  603.                 }
  604.             }
  605.  
  606.             return true;
  607.         }
  608.  
  609.         /// <summary>
  610.         /// ContainsKey check if <see cref="Constants.ALL_KEYS">AllKeys</see> key contains element redIsKey
  611.         /// </summary>
  612.         /// <param name="ckey">redIsKey to search</param>
  613.         /// <returns>true, if cache contains key, otherwise false</returns>
  614.         public override bool ContainsKey(string ckey)
  615.         {
  616.             if (GetAllKeys().Contains(ckey))
  617.             {
  618.                 string redIsString = Db.StringGet(ckey, CommandFlags.None);
  619.                 if (!string.IsNullOrEmpty(redIsString))
  620.                     return true;
  621.             }
  622.  
  623.             return false;
  624.         }
  625.  
  626.         /// <summary>
  627.         /// GetAllKeys returns <see cref="HashSet{string}"/></string> <see cref="_allKeys"/>
  628.         /// </summary>
  629.         /// <returns>returns <see cref="HashSet{string}"/></string> <see cref="_allKeys"/></returns>
  630.         public static HashSet<string> GetAllKeys()
  631.         {
  632.             if (_allKeys == null || _allKeys.Count == 0)
  633.             {
  634.                 string jsonVal = Db.StringGet(ALL_KEYS, CommandFlags.None);
  635.                 string[] keys = (jsonVal != null) ? JsonConvert.DeserializeObject<string[]>(jsonVal) : new string[0];
  636.                 if (keys != null && keys.Length > 0)
  637.                     _allKeys = new HashSet<string>(keys);
  638.             }
  639.  
  640.             return _allKeys;
  641.         }
  642.     }
  643.  
  644.  
  645.     [Serializable]
  646.     public class CacheData
  647.     {
  648.         public int CIndex { get; set; }
  649.         public string CKey { get; set; }
  650.         public string CValue { get; set; }
  651.         public DateTime CTime { get; set; }
  652.         public int CThreadId { get; set; }
  653.  
  654.         public CacheData()
  655.         {
  656.             CIndex = 0;
  657.             CValue = string.Empty;
  658.             CKey = string.Empty;
  659.             CTime = DateTime.MinValue;
  660.             CThreadId = -1;
  661.         }
  662.  
  663.         public CacheData(int cidx, string cval, DateTime ctime) : this()
  664.         {
  665.             CIndex = cidx;
  666.             CKey = string.Concat("Key_", CIndex);
  667.             CValue = cval;
  668.             CTime = ctime;
  669.         }
  670.  
  671.     }
  672.  
  673.     internal class Program
  674.     {
  675.         static void Main(string[] args)
  676.         {
  677.             Console.WriteLine("Hello, World!");
  678.             Task[] taskArray = new Task[512];
  679.             for (int i = 0; i < 256; i++)
  680.             {
  681.                 taskArray[i] = Task.Factory.StartNew((Object obj) =>
  682.                 {
  683.                     CacheData data = obj as CacheData;
  684.                     if (data == null)
  685.                         return;
  686.  
  687.                     string key = "Key_" + i.ToString();
  688.                     data.CThreadId = Thread.CurrentThread.ManagedThreadId;
  689.                     JsonCache.CacheDict.SetValue<CacheData>(key, data);
  690.                     Console.WriteLine($"Task set cache key #{data.CKey} created at {data.CTime} on thread #{data.CThreadId}.");
  691.                 },
  692.                 new CacheData(i, GetRandomString(i), DateTime.Now));
  693.             }
  694.  
  695.             // Task.WaitAll(taskArray);
  696.  
  697.             // var tasks = new List<Task>();
  698.             for (int j = 256; j < 512; j++)
  699.             {
  700.                 taskArray[j] = Task.Factory.StartNew((Object obj) =>
  701.                 {
  702.                     string strkey = obj as string;
  703.                     if (string.IsNullOrEmpty(strkey))
  704.                         strkey = "Key_" + (j - 256).ToString();
  705.  
  706.                     CacheData data = JsonCache.CacheDict.GetValue<CacheData>(strkey);
  707.                     Console.WriteLine($"Task get cache key #{strkey} => {data.CIndex}:{data.CValue} created at {data.CTime} on thread #{Thread.CurrentThread.ManagedThreadId}.");
  708.                 },
  709.                 new StringBuilder(string.Concat("Key_", (j-256)).ToString()));
  710.             }
  711.  
  712.             Task.WaitAll(taskArray);
  713.             // Task.WaitAll(tasks);
  714.             // Parallel.ForEach(tasks, task => { task.Start(); });
  715.             // Task.WhenAll(tasks).ContinueWith(done => { /* Run the other tasks */});
  716.         }
  717.  
  718.         static string GetRandomString(int ix)
  719.         {
  720.             Random random = new Random((DateTime.Now.Millisecond + 1) * (DateTime.Now.Second + 1));
  721.             byte[] buffer = new byte[128];
  722.             random.NextBytes(buffer);
  723.             string randomString = string.Format("{0} \t{1}", ix, Convert.ToBase64String(buffer, 0, buffer.Length));
  724.             return randomString;
  725.         }
  726.  
  727.     }
  728.  
  729. }
  730.  
Add Comment
Please, Sign In to add comment