February 18th, 2009

EOD Journal No Comments »
  • Woke up without water in the house. They turned it off without warning yesterday. No shower. :(
  • Started to implement dashboard functionality for the crystaltech.com control panel.
  • Extremely proud of the new CrystalTech.Menus code.
  • Paid the water bill
  • Started my blog again after a long hiatus
  • Started to brainstorm new design for Airflow Innovations Corp website.
  • Need a new driveshaft for the Magnum. Probably going with a custom piece from Dicks Drive Shaft.

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

Merge To Trunk With Visual SVN

Development No Comments »

I don’t know why I never realized that merging was possible in the GUI of Visual SVN but, alas, I hadn’t. It turns out it’s been right there in front of my face all along! ;)

Here’s a situation I ran into today.

I have a personal branch (personal repo too) for a feature I’m working on. After the feature was complete and all was green, I want to merge the feature into the trunk. It’s really simple.

First thing you’ll want to do is make sure you have committed your changes to your feature branch.

Next go ahead and switch to the trunk of your repository.

switch

Now you can start the merging process by clicking the “Merge” button (duh. ;) ).

merge-button

Now the common TortoiseSVN Merge dialog will popup and you can merge like you’re already used to.

merge-from-branch

-=Code Well