Skip to content

Instantly share code, notes, and snippets.

@maximilian-krauss
Created July 12, 2013 06:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maximilian-krauss/5982424 to your computer and use it in GitHub Desktop.
Save maximilian-krauss/5982424 to your computer and use it in GitHub Desktop.
Very simple object cache with support for object expiration
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace kraussHQ {
public sealed class BlobCacheEntry {
public DateTime ExpireDate { get; set; }
public string Key { get; set; }
public string Identifier { get; set; }
public string FullBlobPath(string storageDirectory) {
return Path.Combine(storageDirectory, string.Format("{0}.xml", Identifier));
}
}
interface IBlobCache {
void Add<T>(string key, T value) where T : class;
void Add<T>(string key, T value, DateTime expireDate) where T : class;
bool KeyExists(string key);
void Remove(string key);
T Retrieve<T>(string key) where T : class;
}
public sealed class ShortTermBlobCache : IBlobCache {
private const string IndexFileName = "BlobCacheIndex.xml";
private const int DefaultObjectLifetimeInHours = 10;
private readonly string _StorageDirectory;
private readonly string _IndexPath;
private List<BlobCacheEntry> _Index;
public ShortTermBlobCache()
: this(Path.Combine(Environment.ExpandEnvironmentVariables("%tmp%"), "BlobCache")) {
}
public ShortTermBlobCache(string storageDirectory) {
_StorageDirectory = storageDirectory;
if (!Directory.Exists(_StorageDirectory))
Directory.CreateDirectory(_StorageDirectory);
_IndexPath = Path.Combine(_StorageDirectory, IndexFileName);
if (File.Exists(_IndexPath)) {
using (var reader = new StreamReader(_IndexPath, Encoding.UTF8)) {
var serializer = new XmlSerializer(typeof(List<BlobCacheEntry>));
_Index = (List<BlobCacheEntry>)serializer.Deserialize(reader);
CleanCache();
}
}
else
_Index = new List<BlobCacheEntry>();
}
private void SaveIndex() {
CleanCache();
using (var writer = new StreamWriter(_IndexPath, false, Encoding.UTF8)) {
var serializer = new XmlSerializer(typeof(List<BlobCacheEntry>));
serializer.Serialize(writer, _Index);
}
}
private void CleanCache() {
for (var i = _Index.Count - 1; i >= 0; i--) {
var blob = _Index[i];
if (blob.ExpireDate > DateTime.MinValue && DateTime.Now > blob.ExpireDate) {
_Index.RemoveAt(i);
var blobPath = blob.FullBlobPath(_StorageDirectory);
if (File.Exists(blobPath))
File.Delete(blobPath);
}
}
}
public void Add<T>(string key, T value) where T : class {
Add<T>(key, value, DateTime.Now.AddHours(DefaultObjectLifetimeInHours));
}
public void Add<T>(string key, T value, DateTime expireDate) where T : class {
if (_Index.Any(b => b.Key == key.ToLowerInvariant()))
throw new ArgumentException("An item with the same key has already been added.");
var blob = new BlobCacheEntry {
Key = key.ToLowerInvariant(),
Identifier = Guid.NewGuid().ToString(),
ExpireDate = expireDate
};
using (var writer = new StreamWriter(blob.FullBlobPath(_StorageDirectory), false, Encoding.UTF8)) {
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(writer, value);
}
_Index.Add(blob);
SaveIndex();
}
public T Retrieve<T>(string key) where T : class {
CleanCache();
SaveIndex();
var blob = _Index.SingleOrDefault(b => b.Key == key.ToLowerInvariant());
if (blob == null)
throw new KeyNotFoundException();
using (var reader = new StreamReader(blob.FullBlobPath(_StorageDirectory), Encoding.UTF8)) {
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(reader);
}
}
public void Remove(string key) {
var blob = _Index.FirstOrDefault(b => b.Key == key.ToLowerInvariant());
if (blob == null)
return;
_Index.Remove(blob);
CleanCache();
SaveIndex();
}
public bool KeyExists(string key) {
return _Index.Any(b => b.Key == key.ToLowerInvariant());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment