ILocalData Again

Development No Comments »

Just a random ‘ah-hah’ momentary update on the last post.

EDIT: After further refactoring, I’m done….for now.

You can download the code here.

Cool ILocalData Implementation

Development 1 Comment »

I’ve always been impressed with Ayende’s LocalData class. I like Ritesh Rao’s vision of local data even more. I loved how he gives the option of application, session, and thread storage. But, I feel like those implementations leave something to be desired from a consumers perspective. So, I’ve come up with a base interface that is inspired by both of the above projects. It also defines an abstract class so I can create new implementations whenever I need to.

The ILocalData interface:

public interface ILocalData
{
    object this[object key] { get; set; }
    T Get(object key);
    bool TryGet(object key, out T value);
    T Set(object key, T value);
    void Add(object key, object value);
    void Remove(object key);
    void Remove(params object[] keys);
    bool Contains(object key);
    void Clear();
}

The IDataBucket interface:

public interface IDataBucket : IEnumerable
{
    object this[object key] { get; set; }
    bool ContainsKey(object key);
    void Remove(object key);
    void Clear();
    void Add(object key, object value);
    bool UseSynchLock { get; set; }
}

The entry point and abstract class:

public class LocalData : BucketData
{
    public LocalData(IDataBucket bucket)
        : base(bucket)
    {}

    public LocalData(Func<IDataBucket> bucketProvider)
        : base(bucketProvider)
    {}
}

public abstract class BucketData : ILocalData, IEnumerable
{
    private static readonly object lockInstance = new object();
    private readonly Func<IDataBucket> bucketProvider;

    protected BucketData(IDataBucket bucket)
        : this(() => bucket)
    {}

    protected BucketData(Func<IDataBucket> bucketProvider)
    {
        this.bucketProvider = bucketProvider;
    }

    public IDataBucket Bucket
    {
        get { return bucketProvider(); }
    }

    public object this[object key]
    {
        get { return Do(() => Bucket[key.ToString()]); }
        set { Do(() => Bucket[key.ToString()] = value); }
    }

    public IEnumerator GetEnumerator()
    {
        return Bucket.GetEnumerator();
    }

    public T Get<T>(object key)
    {
        return Do(() =>
                      {
                          T value;
                          TryGet(key, out value);
                          return value;
                      });
    }

    public bool TryGet<T>(object key, out T value)
    {
        value = default(T);
        object data = Bucket[key.ToString()];

        if (data == null)
            return false;

        if (data is T)
        {
            value = (T) data;
            return true;
        }

        return false;
    }

    public T Set<T>(object key, T value)
    {
        return Do(() =>
                      {
                          Bucket[key.ToString()] = value;
                          return value;
                      });
    }

    public void Add(object key, object value)
    {
        Set(key, value);
    }

    public void Remove(object key)
    {
        if (Contains(key))
            Do(() => Bucket.Remove(key.ToString()));
    }

    public void Remove(params object[] keys)
    {
        foreach (object key in keys)
        {
            Remove(key);
        }
    }

    public bool Contains(object key)
    {
        return Do(() => Bucket[key.ToString()] != null);
    }

    public void Clear()
    {
        Do(() => Bucket.Clear());
    }

    private V Do<V>(Func<V> function)
    {
        if (Bucket.UseSynchLock)
        {
            lock (lockInstance)
            {
                return function();
            }
        }

        return function();
    }

    private void Do(Action action)
    {
        if (Bucket.UseSynchLock)
        {
            lock (lockInstance)
            {
                action();
            }
        }

        action();
    }
}

With the following, I can finally say goodbye to buggy HttpSessionState code in legacy systems and even support session isolation.

public class HttpSessionDataBucket : IDataBucket
{
    private readonly string bucketName;

    public HttpSessionDataBucket() : this(typeof (IDataBucket))
    {}

    public HttpSessionDataBucket(Type forType) : this(forType.FullName)
    {}

    public HttpSessionDataBucket(string bucketName)
    {
        this.bucketName = bucketName;
    }

    private Hashtable Bucket
    {
        get
        {
            HttpSessionState session = HttpContext.Current.Session;
            object ht = session[bucketName];

            if (ht is HashtableDataBucket)
                return (Hashtable) ht;

            session[bucketName] = ht = new Hashtable();

            return ht as Hashtable;
        }
    }

    public object this[object key]
    {
        get { return Bucket[key.ToString()]; }
        set { Bucket[key.ToString()] = value; }
    }

    public bool ContainsKey(object key)
    {
        return Bucket[key.ToString()] != null;
    }

...
}
ILocalData localData = new LocalData(new HttpSessionDataBucket(typeof(IMenuItem)));

This is obviously still evolving but, for now, I love being able to have an ILocalData instance in my domain objects (most likely not session buckets though) and being able to call “localData.Clear();” without messing up the rest of the system.

It’s amazing how far we’ll go sometimes. Schwew! ;)

-=Code Well