|
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Transactions;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Instrumentation;
namespace CachingAppBlock.Transactional
{
/// <summary>
/// This is main class for transactional caching
/// </summary>
public class TransactionalCacheManager : ICacheManager, IEnlistmentNotification
{
private readonly ICacheManager cacheManager;
[ThreadStatic]
private static ThreadSafeDictionary<string, CommandItem> transactionalRepository;
public TransactionalCacheManager()
{
cacheManager = CacheFactory.GetCacheManager();
}
#region ICacheManager Members
void ICacheManager.Add(string key, object value, CacheItemPriority scavengingPriority, ICacheItemRefreshAction refreshAction, params ICacheItemExpiration[] expirations)
{
throw new NotSupportedException("Transaction cache does not support custom priorities and refresh actions");
}
void ICacheManager.Add(string key, object value)
{
TransactionalLock.Lock(key);
Enlist(key, Operation.Added, value, () => cacheManager.Add(key, value, CacheItemPriority.NotRemovable, null, null));
}
/// <summary>
/// This method is not transactional
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
bool ICacheManager.Contains(string key)
{
if(Transaction.Current != null)
{
if(CurrentTransactionalRepository.Keys.Contains(key))
{
return CurrentTransactionalRepository[key].Operation == Operation.Added;
}
}
return cacheManager.Contains(key);
}
int ICacheManager.Count
{
get { return cacheManager.Count; }
}
void ICacheManager.Flush()
{
throw new NotImplementedException("Not implemented yet");
}
/// <summary>
/// here we do return copy of data to protect consistency. if you need to updated your data, use Add method which will replace old object with the new one
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
object ICacheManager.GetData(string key)
{
TransactionalLock.Lock(key);
if (Transaction.Current != null)
{
if (CurrentTransactionalRepository.ContainsKey(key))
{
if (CurrentTransactionalRepository[key].Operation == Operation.Added)
{
return CurrentTransactionalRepository[key].Value;
}
else
{
return null;
}
}
return AttackOfTheClonesFactory.Clone(cacheManager.GetData(key));
}
return cacheManager.GetData(key);
}
void ICacheManager.Remove(string key)
{
TransactionalLock.Lock(key);
Enlist(key, Operation.Removed, null, () => cacheManager.Remove(key));
}
object ICacheManager.this[string key]
{
get { return ((ICacheManager)this).GetData(key); }
}
#endregion
#region IEnlistmentNotification Members
void IEnlistmentNotification.Commit(Enlistment enlistment)
{
foreach(var commandItem in CurrentTransactionalRepository.Values)
{
commandItem.Command.Invoke();
}
enlistment.Done();
}
void IEnlistmentNotification.InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
void IEnlistmentNotification.Rollback(Enlistment enlistment)
{
enlistment.Done();
}
#endregion
void Enlist(string key, Operation operation, object value, Action command)
{
if(Transaction.Current != null)
{
Debug.Assert(Transaction.Current.TransactionInformation.Status == TransactionStatus.Active);
//notify transaction manager that we want to participate in transaction
Transaction.Current.EnlistVolatile(this, EnlistmentOptions.None);
CurrentTransactionalRepository[key] = new CommandItem(operation, value, command);
}
else
{
command.Invoke();
}
}
private ThreadSafeDictionary<string, CommandItem> CurrentTransactionalRepository
{
get
{
if(transactionalRepository == null)
{
transactionalRepository = new ThreadSafeDictionary<string, CommandItem>();
//here we make sure that our transaction repository will be cleaned after transaction complition
Transaction.Current.TransactionCompleted +=
(object sender, TransactionEventArgs e) => CleanTransactionRepository(Thread.CurrentThread);
}
return transactionalRepository;
}
}
private void CleanTransactionRepository(Thread t)
{
transactionalRepository = null;
}
private class CommandItem
{
public Operation Operation { get; private set; }
public object Value { get; private set; }
public Action Command { get; private set; }
public CommandItem(Operation operation, object value, Action command)
{
Operation = operation;
Value = value;
Command = command;
}
}
private enum Operation
{
Added,
Removed
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
The views expressed in my articles are mine and do not necessarily reflect the views of my employer.
if(youWantToContactMe)
{
SendMessage(string.Format("{0}@{1}.com", "liptchinski_vit", "yahoo"));
}
More info in my LinkedIn profile:
http://www.linkedin.com/in/vitaliyliptchinsky