andrew4582

File Object Cache

Nov 22nd, 2013
318
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.94 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.Caching;
  7. using System.Runtime.Serialization;
  8. using System.Runtime.Serialization.Formatters.Binary;
  9. using System.Threading;
  10.  
  11. namespace Common.Caching
  12. {
  13.        /// <summary>Uses <see cref="System.Runtime.Caching.MemoryCache"/> cache. Used on local client or server</summary>
  14.     public class FileObjectCache : ObjectCache
  15.     {
  16.         #region static & const
  17.  
  18.         const string File_Extention = ".xml";
  19.         const string Internal_Key_Prefix = "FileObjectCache_";
  20.  
  21.         static readonly FileObjectCache _default;
  22.         public static FileObjectCache Default
  23.         {
  24.             get { return _default; }
  25.         }
  26.  
  27.         static FileObjectCache()
  28.         {
  29.             _default = new FileObjectCache();
  30.         }
  31.  
  32.         #endregion
  33.  
  34.         string _cacheName;
  35.         long _memoryCount;
  36.  
  37.         public string CacheDirectory { get; private set; }
  38.  
  39.         public FileObjectCache() : this(null, null) { }
  40.         public FileObjectCache(string cacheName) : this(cacheName, null) { }
  41.         public FileObjectCache(string cacheName, string directory)
  42.         {
  43.             _cacheName = cacheName;
  44.             this.CacheDirectory = directory;
  45.  
  46.             if (string.IsNullOrWhiteSpace(this.CacheDirectory))
  47.                 this.CacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "_FileCache");
  48.  
  49.             if (!string.IsNullOrWhiteSpace(_cacheName))
  50.                 this.CacheDirectory = Path.Combine(this.CacheDirectory, _cacheName);
  51.  
  52.             EnsureDestinationDirectory(this.CacheDirectory);
  53.         }
  54.  
  55.         MemoryCache MemCache
  56.         {
  57.             get { return MemoryCache.Default; }
  58.         }
  59.  
  60.         string GetInternalCacheKey(string key, string regionName = null)
  61.         {
  62.             return string.Format("{0}_{1}", Internal_Key_Prefix, key);
  63.         }
  64.  
  65.         string GetCacheFile(string key, string regionName)
  66.         {
  67.             //if we want to store each cache item in a separate directory
  68.             //string dirpath = GetCacheItemDirectory(key, regionName);
  69.             //EnsureDestinationDirectory(dirpath);
  70.  
  71.             string fname = string.Format("{0}{1}", key.GetHashCode(), File_Extention);
  72.             string fpath = Path.Combine(this.CacheDirectory, fname);
  73.             return fpath;
  74.         }
  75.  
  76.         string GetCacheItemDirectory(string key, string regionName)
  77.         {
  78.             string dirpath = Path.Combine(this.CacheDirectory, string.Format("{0}", key.GetHashCode()));
  79.             return dirpath;
  80.         }
  81.  
  82.         FileCacheItem ReadFileCacheItem(string fpath)
  83.         {
  84.             try
  85.             {
  86.                 var citem = SerializeFromBytes<FileCacheItem>(File.ReadAllBytes(fpath));
  87.                 return citem;
  88.             }
  89.             catch (Exception error)
  90.             {
  91.                 Debug.WriteLine(string.Format("FileObjectCache: There was an error reading the file '{0}'; Error: {1}", fpath, error.Message));
  92.                 return null;
  93.             }
  94.         }
  95.  
  96.         static byte[] SerializeToBytes(object source)
  97.         {
  98.             if (!source.GetType().IsSerializable)
  99.                 throw new ArgumentException("The type must be serializable.", "source");
  100.  
  101.             IFormatter formatter = new BinaryFormatter();
  102.             using (var stream = new MemoryStream())
  103.             {
  104.                 formatter.Serialize(stream, source);
  105.                 stream.Seek(0, SeekOrigin.Begin);
  106.                 return stream.ToArray();
  107.             }
  108.         }
  109.  
  110.         static T SerializeFromBytes<T>(byte[] buffer)
  111.         {
  112.             if (!typeof(T).IsSerializable)
  113.                 throw new ArgumentException("The type must be serializable.", "T");
  114.  
  115.             IFormatter formatter = new BinaryFormatter();
  116.             using (var stream = new MemoryStream(buffer))
  117.             {
  118.                 if (stream.Position > 0)
  119.                     stream.Seek(0, SeekOrigin.Begin);
  120.                 return (T)formatter.Deserialize(stream);
  121.             }
  122.         }
  123.  
  124.         static void EnsureDestinationDirectory(string path)
  125.         {
  126.             string dirpath = path;
  127.             if (Path.HasExtension(path))
  128.                 dirpath = Path.GetDirectoryName(path);
  129.  
  130.             if (!Directory.Exists(dirpath))
  131.                 Directory.CreateDirectory(dirpath);
  132.         }
  133.  
  134.         static bool DeleteSafe(string filepath, out Exception error)
  135.         {
  136.             error = null;
  137.             try
  138.             {
  139.                 if (!File.Exists(filepath))
  140.                     return true;
  141.                 File.Delete(filepath);
  142.                 return true;
  143.             }
  144.             catch (Exception deleteError)
  145.             {
  146.                 error = deleteError;
  147.                 try
  148.                 {
  149.                     File.SetAttributes(filepath, FileAttributes.Normal);
  150.                     File.Delete(filepath);
  151.                     error = null;
  152.                     return true;
  153.                 }
  154.                 catch (Exception atterror)
  155.                 {
  156.                     error = atterror;
  157.                     return false;
  158.                 }
  159.             }
  160.         }
  161.  
  162.         public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
  163.         {
  164.             var cacheData = Get(key, regionName);
  165.             if (cacheData == null)
  166.                 Set(key, value, policy, regionName);
  167.             return cacheData;
  168.         }
  169.  
  170.         public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
  171.         {
  172.             var citem = GetCacheItem(value.Key, value.RegionName);
  173.             if (citem == null)
  174.                 Set(citem, policy);
  175.             return citem;
  176.         }
  177.  
  178.         public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
  179.         {
  180.             var cacheData = Get(key, regionName);
  181.             if (cacheData == null)
  182.                 Set(key, value, absoluteExpiration, regionName);
  183.             return cacheData;
  184.         }
  185.  
  186.         public override bool Contains(string key, string regionName = null)
  187.         {
  188.             return Get(key, regionName) != null;
  189.         }
  190.  
  191.         public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
  192.         {
  193.             return null;
  194.         }
  195.  
  196.         public override DefaultCacheCapabilities DefaultCacheCapabilities
  197.         {
  198.             get
  199.             {
  200.                 return DefaultCacheCapabilities.OutOfProcessProvider
  201.                     | DefaultCacheCapabilities.AbsoluteExpirations;
  202.             }
  203.         }
  204.  
  205.         public override object Get(string key, string regionName = null)
  206.         {
  207.             string internalKey = GetInternalCacheKey(key);
  208.  
  209.             FileCacheItem fitem = MemCache.Get(internalKey, regionName) as FileCacheItem;
  210.             if (fitem == null)
  211.             {
  212.                 //check to see if we have the data on file
  213.                 string filepath = GetCacheFile(key, regionName);
  214.                 if (File.Exists(filepath))
  215.                 {
  216.                     fitem = ReadFileCacheItem(filepath);
  217.  
  218.                     if (fitem != null && fitem.Value != null)
  219.                     {
  220.                         //check for expiration
  221.                         if (DateTimeOffset.Now >= fitem.AbsoluteExpiration)
  222.                         {
  223.                             Remove(key, regionName);
  224.                             fitem = null;
  225.                         }
  226.                         else
  227.                         {
  228.                             //add to local memory
  229.                             CacheItemPolicy policy = new CacheItemPolicy();
  230.                             policy.AbsoluteExpiration = fitem.AbsoluteExpiration;
  231.                             MemCache.Set(internalKey, fitem, policy, regionName);
  232.                         }
  233.                     }
  234.                 }
  235.             }
  236.             if (fitem != null && fitem.Value != null)
  237.                 return fitem.Value.GetValue();
  238.             else
  239.                 return null;
  240.         }
  241.  
  242.         public override CacheItem GetCacheItem(string key, string regionName = null)
  243.         {
  244.             string internalKey = GetInternalCacheKey(key);
  245.             var citem = MemCache.GetCacheItem(internalKey, regionName);
  246.             if (citem == null)
  247.             {
  248.                 citem = MemCache.GetCacheItem(key, regionName);
  249.                 if (citem == null)
  250.                 {
  251.                     var value = Get(key, regionName);
  252.                     if (value != null)
  253.                         citem = new CacheItem(key, value, regionName);
  254.                 }
  255.             }
  256.             return citem;
  257.         }
  258.  
  259.         public override long GetCount(string regionName = null)
  260.         {
  261.             long mcount = Interlocked.Read(ref _memoryCount);
  262.  
  263.             if (mcount <= 0 && Directory.Exists(CacheDirectory))
  264.             {
  265.                 long fcount = Directory.GetFiles(CacheDirectory, string.Format("*{0}", File_Extention)).LongLength;
  266.                 if (fcount != mcount)
  267.                     Interlocked.Exchange(ref _memoryCount, fcount);
  268.             }
  269.             return mcount;
  270.         }
  271.  
  272.         protected override System.Collections.Generic.IEnumerator<KeyValuePair<string, object>> GetEnumerator()
  273.         {
  274.             long mcount = Interlocked.Read(ref _memoryCount);
  275.  
  276.             if (mcount <= 0 && Directory.Exists(CacheDirectory))
  277.             {
  278.                 //iterate though the cache directory
  279.                 foreach (var fpath in Directory.EnumerateFiles(CacheDirectory, string.Format("*{0}", File_Extention)))
  280.                 {
  281.                     var citem = ReadFileCacheItem(fpath);
  282.                     if (citem == null)
  283.                         continue;
  284.  
  285.                     string key = citem.Key;
  286.                     object value = citem.Value.GetValue();
  287.  
  288.                     var internalKey = GetInternalCacheKey(key);
  289.  
  290.                     yield return new KeyValuePair<string, object>(key, value);
  291.                 }
  292.             }
  293.             else
  294.             {
  295.                 foreach (var citem in MemCache)
  296.                 {
  297.                     if (citem.Key.StartsWith(Internal_Key_Prefix))
  298.                         yield return citem;
  299.                 }
  300.             }
  301.         }
  302.  
  303.         public override System.Collections.Generic.IDictionary<string, object> GetValues(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
  304.         {
  305.             Dictionary<string, object> dict = new Dictionary<string, object>(keys.Count());
  306.             foreach (string key in keys)
  307.                 dict[key] = Get(key, regionName);
  308.             return dict;
  309.         }
  310.  
  311.         public override string Name
  312.         {
  313.             get { return _cacheName; }
  314.         }
  315.  
  316.         public override object Remove(string key, string regionName = null)
  317.         {
  318.             string internalKey = GetInternalCacheKey(key);
  319.  
  320.             object cacheData = null;
  321.             if (MemCache.Contains(internalKey, regionName))
  322.             {
  323.                 var fitem = MemCache.Remove(internalKey, regionName) as FileCacheItem;
  324.                 if (fitem != null)
  325.                     cacheData = fitem.Value.GetValue();
  326.  
  327.                 Interlocked.Decrement(ref _memoryCount);
  328.             }
  329.             //check to see if we have the data on file
  330.             string filepath = GetCacheFile(key, regionName);
  331.             if (File.Exists(filepath))
  332.             {
  333.                 //delete if exists
  334.                 Exception deleteError = null;
  335.                 if (!DeleteSafe(filepath, out deleteError))
  336.                     throw deleteError;
  337.             }
  338.             return cacheData;
  339.         }
  340.  
  341.         public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
  342.         {
  343.             SetInternal(new FileCacheItem(key, value, regionName), policy, regionName);
  344.         }
  345.  
  346.         public override void Set(CacheItem item, CacheItemPolicy policy)
  347.         {
  348.             SetInternal(new FileCacheItem(item), policy);
  349.         }
  350.  
  351.         public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
  352.         {
  353.             CacheItemPolicy policy = new CacheItemPolicy();
  354.             policy.AbsoluteExpiration = absoluteExpiration;
  355.             SetInternal(new FileCacheItem(key, value, regionName), policy, regionName);
  356.         }
  357.  
  358.         void SetInternal(FileCacheItem fitem, CacheItemPolicy policy, string regionName = null)
  359.         {
  360.             string key = fitem.Key;
  361.             object value = fitem.Value;
  362.             string internalKey = GetInternalCacheKey(key);
  363.  
  364.             //remove any existing items
  365.             this.Remove(key, regionName);
  366.  
  367.             fitem.AbsoluteExpiration = policy.AbsoluteExpiration;
  368.  
  369.             //save to file
  370.             string filepath = GetCacheFile(key, regionName);
  371.  
  372.             byte[] contents = SerializeToBytes(fitem);
  373.             File.WriteAllBytes(filepath, contents);
  374.  
  375.             //add to memory
  376.             if (fitem.OriginalCacheItem == null)
  377.                 MemCache.Set(internalKey, fitem, policy, regionName);
  378.             else
  379.             {
  380.                 MemCache.Set(fitem.OriginalCacheItem, policy);
  381.             }
  382.  
  383.             Interlocked.Increment(ref _memoryCount);
  384.         }
  385.  
  386.         public override object this[string key]
  387.         {
  388.             get { return Get(key); }
  389.             set { Set(key, value, DateTimeOffset.MaxValue); }
  390.         }
  391.  
  392.     }
  393.  
  394.     /// <summary>
  395.     /// Represents an individual cache entry in the cache. Same as <see cref="CacheItem"/>, but marked as serializable
  396.     /// </summary>
  397.     [Serializable]
  398.     public class FileCacheItem
  399.     {
  400.         [NonSerialized]
  401.         CacheItem _originalCacheItem;
  402.  
  403.         /// <summary>
  404.         /// Initializes a new <see cref="T:System.Runtime.Caching.CacheItem"/> instance using the specified key of a cache entry.
  405.         /// </summary>
  406.         public FileCacheItem() : this(null, null) { }
  407.  
  408.         /// <summary>
  409.         /// Initializes a new <see cref="T:System.Runtime.Caching.CacheItem"/> instance using the specified key of a cache entry.
  410.         /// </summary>
  411.         public FileCacheItem(CacheItem cacheItem)
  412.             : this(cacheItem.Key, cacheItem.Value, cacheItem.RegionName)
  413.         {
  414.             _originalCacheItem = cacheItem;
  415.         }
  416.  
  417.         /// <summary>
  418.         /// Initializes a new <see cref="T:System.Runtime.Caching.CacheItem"/> instance using the specified key of a cache entry.
  419.         /// </summary>
  420.         /// <param name="key">A unique identifier for a <see cref="T:System.Runtime.Caching.CacheItem"/> entry. </param>
  421.         public FileCacheItem(string key) : this(key, null) { }
  422.  
  423.         /// <summary>
  424.         /// Initializes a new <see cref="T:System.Runtime.Caching.CacheItem"/> instance using the specified key and a value of the cache entry.
  425.         /// </summary>
  426.         /// <param name="key">A unique identifier for a <see cref="T:System.Runtime.Caching.CacheItem"/> entry.</param><param name="value">The data for a <see cref="T:System.Runtime.Caching.CacheItem"/> entry.</param>
  427.         public FileCacheItem(string key, object value) : this(key, value, null) { }
  428.  
  429.         /// <summary>
  430.         /// Initializes a new <see cref="T:System.Runtime.Caching.CacheItem"/> instance using the specified key, value, and region of the cache entry.
  431.         /// </summary>
  432.         /// <param name="key">A unique identifier for a <see cref="T:System.Runtime.Caching.CacheItem"/> entry.</param><param name="value">The data for a <see cref="T:System.Runtime.Caching.CacheItem"/> entry.</param><param name="regionName">The name of a region in the cache that will contain the <see cref="T:System.Runtime.Caching.CacheItem"/> entry.</param>
  433.         public FileCacheItem(string key, object value, string regionName)
  434.         {
  435.             this.Key = key;
  436.             this.Value = new SerializableObjectWrapper(value);
  437.             this.RegionName = regionName;
  438.             this.AbsoluteExpiration = DateTimeOffset.MaxValue;
  439.         }
  440.  
  441.         public CacheItem OriginalCacheItem
  442.         {
  443.             get { return _originalCacheItem; }
  444.             set { _originalCacheItem = value; }
  445.         }
  446.  
  447.         /// <summary>
  448.         /// Gets or sets a unique identifier for a <see cref="T:System.Runtime.Caching.CacheItem"/> instance.
  449.         /// </summary>
  450.         ///
  451.         /// <returns>
  452.         /// The identifier for a <see cref="T:System.Runtime.Caching.CacheItem"/> instance.
  453.         /// </returns>
  454.         public string Key { get; set; }
  455.         /// <summary>
  456.         /// Gets or sets the data for a <see cref="T:System.Runtime.Caching.CacheItem"/> instance.
  457.         /// </summary>
  458.         ///
  459.         /// <returns>
  460.         /// The data for a <see cref="T:System.Runtime.Caching.CacheItem"/> instance. The default is null.
  461.         /// </returns>
  462.         public SerializableObjectWrapper Value { get; set; }
  463.  
  464.         /// <summary>
  465.         /// Gets or sets the name of a region in the cache that contains a <see cref="T:System.Runtime.Caching.CacheItem"/> entry.
  466.         /// </summary>
  467.         ///
  468.         /// <returns>
  469.         /// The name of a region in a cache. The default is null.
  470.         /// </returns>
  471.         public string RegionName { get; set; }
  472.  
  473.         /// <summary>
  474.         /// Gets or sets a value that indicates whether a cache entry should be evicted after a specified duration.
  475.         /// </summary>
  476.         public DateTimeOffset AbsoluteExpiration { get; set; }
  477.     }
  478.  
  479.     /// <summary>Wraps objects that aren't marked as serializable and uses Xml serialization to serialize and deserialize them</summary>
  480.     [Serializable]
  481.     public class SerializableObjectWrapper : ISerializable
  482.     {
  483.         [NonSerialized]
  484.         object obj;
  485.  
  486.         string objxml;
  487.         Type objType;
  488.  
  489.         public Type ObjectType
  490.         {
  491.             get { return objType; }
  492.         }
  493.  
  494.         protected SerializableObjectWrapper(SerializationInfo info, StreamingContext context)
  495.         {
  496.             objxml = (string)info.GetValue("objxml", typeof(string));
  497.             objType = (Type)info.GetValue("objType", typeof(Type));
  498.         }
  499.  
  500.         public SerializableObjectWrapper(object core)
  501.         {
  502.             this.obj = core;
  503.             if (core != null)
  504.                 this.objType = core.GetType();
  505.         }
  506.  
  507.         public object GetValue()
  508.         {
  509.             if (obj == null)
  510.             {
  511.                 if (string.IsNullOrWhiteSpace(objxml))
  512.                     return null;
  513.  
  514.                 Type obtype = this.ObjectType;
  515.                 if (obtype == null)
  516.                     obtype = typeof(object);
  517.                 obj = Deserialize(objxml, obtype);
  518.             }
  519.             return obj;
  520.         }
  521.  
  522.         public void GetObjectData(SerializationInfo info, StreamingContext context)
  523.         {
  524.             info.AddValue("objxml", Serialize(obj));
  525.             info.AddValue("objType", objType);
  526.         }
  527.  
  528.         static string Serialize(object xmlObject, bool encodeInBase64 = false)
  529.         {
  530.             using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
  531.             {
  532.                 System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(xmlObject.GetType());
  533.                 System.Xml.Serialization.XmlSerializerNamespaces xmlNamespace = new System.Xml.Serialization.XmlSerializerNamespaces();
  534.                 xmlNamespace.Add(string.Empty, string.Empty);
  535.                 xs.Serialize(memoryStream, xmlObject, xmlNamespace);
  536.                 System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
  537.                 if (encodeInBase64)
  538.                     return Convert.ToBase64String(System.Text.ASCIIEncoding.Unicode.GetBytes(encoding.GetString(memoryStream.ToArray())));
  539.                 else
  540.                     return encoding.GetString(memoryStream.ToArray());
  541.             }
  542.         }
  543.  
  544.         static object Deserialize(string xml, Type objtype)
  545.         {
  546.             bool decodeInBase64 = false;
  547.             string data = xml;
  548.             if (decodeInBase64)
  549.                 data = System.Text.ASCIIEncoding.Unicode.GetString(Convert.FromBase64String(xml));
  550.             System.Xml.Serialization.XmlSerializer xs = System.Xml.Serialization.XmlSerializer.FromTypes(new Type[] { objtype })[0];
  551.             System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
  552.             using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(encoding.GetBytes(data)))
  553.             {
  554.                 System.Xml.XmlTextWriter xmlTextWriter = new System.Xml.XmlTextWriter(memoryStream, System.Text.Encoding.UTF8);
  555.                 return xs.Deserialize(memoryStream);
  556.             }
  557.         }
  558.     }
  559. }
Advertisement
Add Comment
Please, Sign In to add comment