using System;
using System.Transactions;
using Microsoft.Data.Caching;
using System.Collections.Generic;
namespace VelocityTools
{ /// <summary>
/// This class is a wrapper on Velocity CacheData that support transaction on "Put" actions.
/// It's also enable a Bulk insertion by allow multiple "Put" actions in single transaction scope.
/// When a user "Put" data into the cache, the data stored in the class instance.
/// Only when the transaction scope is completed, the data will be "Put" into the velocity cache.
/// </summary>
/// <typeparam name="TDataCache">A Velocity DataCache</typeparam>
public class TransactionalDataCache <TDataCache>: IEnlistmentNotification where TDataCache:DataCache
{
/// <summary>
/// Initiate the class.
/// Exception will thrown if no transaction scope exist.
/// </summary>
/// <param name="dataCache">A Velocity DataCache</param>
public TransactionalDataCache(TDataCache dataCache)
{ if (Transaction.Current == null)
throw new Exception("No TransctionScope exist");
Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
this.LocalCache = dataCache;
this.CachedDataList = new List<ItemToCache>();
}
/// <summary>
/// The velocity CacheData instance
/// </summary>
private TDataCache LocalCache { get; set; }
/// <summary>
/// This list hold all the data the the user want to cache.
/// </summary>
private List<ItemToCache> CachedDataList {get;set;}
/// <summary>
/// Adds or replaces an object in the cache.
/// </summary>
/// <param name="key">The unique value that is used to identify the object in the cache.</param>
/// <param name="value">The object to add or replace.</param>
public void Put(string key, object value)
{ this.Put(key, value, null);
}
/// <summary>
/// Adds or replaces an object in the specified region.
/// </summary>
/// <param name="key">The unique value that is used to identify the object in the cache.</param>
/// <param name="value">The object to add or replace.</param>
/// <param name="region">The name of the region the object resides in.</param>
public void Put(string key, object value, string region)
{ this.CachedDataList.Add(new ItemToCache() { Key = key, Value = value , Region = region }); }
#region IEnlistmentNotification Members
public void InDoubt(Enlistment enlistment)
{ enlistment.Done();
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{ try
{ ///try to "Put" the data into velocity cache.
///each item that is "Put" in the velocty cache, marked as "IsCached"=true.
this.PutItemsToCache();
preparingEnlistment.Prepared();
}
catch(Exception ex)
{ ///remove all the items that already "Put" in the Velocity cache.
this.RemoveItemsFromCache();
preparingEnlistment.ForceRollback(ex);
}
}
public void Commit(Enlistment enlistment)
{ ///clear the current instance,
///so if the user will try to work with this instance after commit or rollback, he will failed.
this.ClearInstance();
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{ ///remove all the items that already "Put" into Velocity cache.
this.RemoveItemsFromCache();
///clear the current instance,
///so if the user will try to work with this instance after commit or rollback, he will failed.
this.ClearInstance();
enlistment.Done();
}
#endregion
/// <summary>
/// "Put" all the items into the Velocity cache.
/// Each item that successfully added, marked as "IsCached"=true
/// </summary>
private void PutItemsToCache()
{ foreach (var data in this.CachedDataList)
this.Put(data);
}
/// <summary>
/// "Put" an item into the Velocity cache.
/// if the item successfully added, marked as "IsCached"=true
/// </summary>
/// <param name="data">An Item to "Put" into the velocty cache</param>
private void Put(ItemToCache data)
{ if (data.Region == null)
this.LocalCache.Put(data.Key, data.Value);
else
this.LocalCache.Put(data.Key, data.Value, data.Region);
data.IsCached = true;
}
/// <summary>
/// Remove all the items that marked as "IsCached" = true
/// from the velocity cache.
/// </summary>
private void RemoveItemsFromCache()
{ foreach (var data in this.CachedDataList)
if(data.IsCached)
this.Remove(data);
}
/// <summary>
/// Remove an Item from the Velocity cache.
/// </summary>
/// <param name="data">An Item to remove from velocity cache</param>
private void Remove(ItemToCache data)
{ if (data.Region == null)
this.LocalCache.Remove(data.Key);
else
this.LocalCache.Remove(data.Key, data.Region);
}
/// <summary>
///clear the current instance,
///so if the user will try to work with this instance after commit or rollback, he will failed.
/// </summary>
private void ClearInstance()
{ this.LocalCache = null;
this.CachedDataList.Clear();
this.CachedDataList = null;
}
/// <summary>
/// This class represent data to cache.
/// </summary>
private class ItemToCache
{ public ItemToCache()
{ this.IsCached = false;
}
public string Key { get; set; } public string Region { get; set; } public object Value { get; set; } public bool IsCached { get; set; } }
}
}