Analytics

Sunday, August 15, 2010

Dictionary Adapter for HttpSessionState

For nearly every web application I've written in the past few years, whenever I need to access the HttpSessionState object, I create a wrapper class to wrap the Session with strongly typed properties, so I do not need to deal with the magic string keys and casting the values. Today, I was about to do the same thing, when I realized that I could probably use the DictionaryAdapter from the Castle Project to automatically create an implementation of this class.

If you've never used the DictionaryAdapter before, basically it lets you create a simple interface with only properties with getters and setters, and you can create an implementation of that interface by wrapping anything that implements IDictionary or NameValueCollection. For example, if you had an interface named INeedAnImplentation, you could create the implementation as easily as this:

new DictionaryAdapterFactory().GetAdapter<INeedAnImplentation>(new HashSet());

So, since HttpSessionState acts like a dictionary, I figured I could wrap the session directly. Unfortunately, HttpSessionState does not implement IDictionary or NameValueCollection. No fear, the adapter pattern to the rescue. Just like the DictionaryAdapter can make an IDictionary look like your own interface, I can create an adapter for HttpSessionState to make it look like an IDictionary. Here is that adapter:

public class SessionDictionary : IDictionary<string,object>, IDictionary
{
readonly HttpSessionState sessionState;
readonly CollectionAdapter<string> keysAdapter;
readonly CollectionAdapter<object> valuesAdapter;

public SessionDictionary(HttpSessionState sessionState)
{
this.sessionState = sessionState;
keysAdapter
= new CollectionAdapter<string>(sessionState.Keys);
valuesAdapter
= new CollectionAdapter<object>(sessionState);
}

public bool ContainsKey(string key)
{
return keysAdapter.Contains(key);
}

public void Add(string name, object value)
{
sessionState.Add(name, value);
}

public bool Remove(string name)
{
if (!ContainsKey(name)) return false;
sessionState.Remove(name);
return true;
}

public bool TryGetValue(string key, out object value)
{
if (ContainsKey(key))
{
value
= this[key];
return true;
}

value
= null;
return false;
}

public void Add(KeyValuePair<string, object> item)
{
Add(item.Key, item.Value);
}

public bool Contains(object key)
{
return ContainsKey((string) key);
}

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

public void Clear()
{
sessionState.Clear();
}

IDictionaryEnumerator IDictionary.GetEnumerator()
{
return new DictionaryEnumerator<string, object>(KeyValueEnumerable().GetEnumerator());
}

public void Remove(object key)
{
Remove((
string) key);
}

object IDictionary.this[object key]
{
get { return this[(string) key]; }
set { this[(string) key] = value; }
}

public bool Contains(KeyValuePair<string, object> item)
{
if (!ContainsKey(item.Key)) return false;
return Equals(this[item.Key], item.Value);
}

public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
CopyTo((Array)array, arrayIndex);
}

public bool Remove(KeyValuePair<string, object> item)
{
if (!Contains(item)) return false;
return Remove(item.Key);
}

IEnumerator
<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
return KeyValueEnumerable().GetEnumerator();
}

IEnumerable
<KeyValuePair<string, object>> KeyValueEnumerable()
{
return Keys.Select(key => new KeyValuePair<string, object>(key, this[key]));
}

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

public object this[string name]
{
get { return sessionState[name]; }
set { sessionState[name] = value; }
}

public void CopyTo(Array array, int index)
{
sessionState.CopyTo(array, index);
}

public int Count
{
get { return sessionState.Count; }
}

public object SyncRoot
{
get { return sessionState.SyncRoot; }
}

public bool IsSynchronized
{
get { return sessionState.IsSynchronized; }
}

public ICollection<string> Keys
{
get { return keysAdapter; }
}

ICollection IDictionary.Values
{
get { return valuesAdapter; }
}

ICollection IDictionary.Keys
{
get { return keysAdapter; }
}

public ICollection<object> Values
{
get { return valuesAdapter; }
}

public bool IsReadOnly
{
get { return sessionState.IsReadOnly; }
}

public bool IsFixedSize
{
get { return false; }
}
}


While implementing that adapter, I realized I needed two more adapters. One to expose the keys and values as ICollection implementations, and another to implement the IDictionaryEnumerator interface. Here are those implementations:


public class CollectionAdapter<T> : ICollection<T>, ICollection
{
readonly ICollection collection;

public void CopyTo(Array array, int index)
{
collection.CopyTo(array, index);
}

public object SyncRoot
{
get { return collection.SyncRoot; }
}

public bool IsSynchronized
{
get { return collection.IsSynchronized; }
}

public CollectionAdapter(ICollection collection)
{
this.collection = collection;
}

IEnumerator
<T> IEnumerable<T>.GetEnumerator()
{
return collection.Cast<T>().GetEnumerator();
}

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

public void Add(T item)
{
throw new NotSupportedException();
}

public void Clear()
{
throw new NotSupportedException();
}

public bool Contains(T item)
{
return collection.Cast<T>().Any(x => Equals(x, item));
}

public void CopyTo(T[] array, int arrayIndex)
{
collection.CopyTo(array, arrayIndex);
}

public bool Remove(T item)
{
throw new NotSupportedException();
}

public int Count
{
get { return collection.Count; }
}

public bool IsReadOnly
{
get { return true; }
}
}

public class DictionaryEnumerator<TKey,TValue> : IDictionaryEnumerator, IDisposable
{
readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;

public DictionaryEnumerator(IEnumerator<KeyValuePair<TKey,TValue>> enumerator)
{
this.enumerator = enumerator;
}

public void Dispose()
{
enumerator.Dispose();
}

public bool MoveNext()
{
return enumerator.MoveNext();
}

public void Reset()
{
enumerator.Reset();
}

public object Current
{
get { return enumerator.Current; }
}

public object Key
{
get { return enumerator.Current.Key; }
}

public object Value
{
get { return enumerator.Current.Value; }
}

public DictionaryEntry Entry
{
get { return new DictionaryEntry(Key, Value); }
}
}

Once that was complete, I could now use the Castle DictionaryAdapter to create my interface. I only wanted to create it once, so I registered my interface with StructureMap so it would automatically create an instance for each HttpContext:


public class WebRegistry : Registry
{
public WebRegistry()
{
var dictionaryAdapterFactory
= new DictionaryAdapterFactory();
For
<IWebSession>()
.HttpContextScoped()
.Use(()
=> dictionaryAdapterFactory.GetAdapter<IWebSession>(new SessionDictionary(HttpContext.Current.Session)));
}
}


Where the IWebSession is defined like so:


public interface IWebSession
{
SearchRequest LastSearchRequest {
get; set; }
}

Then to access the session, I only need to make it a dependency on any class that needs to use it. For example, to access it in my FubuMVC controller, I add the IWebSession to the constructor arguments. Then I can set and get the session variables by using the properties on the IWebSession instance:


public class SearchController
{
readonly ISearchService searchService;
readonly IWebSession webSession;

public SearchController(ISearchService searchService, IWebSession webSession)
{
this.searchService = searchService;
this.webSession = webSession;
}

public SearchResponse SearchResults(SearchInput searchInput)
{
var searchRequest
= webSession.LastSearchRequest
?? new SearchRequest();
return searchService(searchRequest, searchInput.Page);
}

public FubuContinuation SearchSuppliers(SearchRequest searchRequest)
{
webSession.LastSearchRequest
= searchRequest;
return FubuContinuation.RedirectTo(new SearchInput{Page = 1});
}
}

The full source code for these files is available as a Gist on GitHub.com:
http://gist.github.com/524832

Please let me know if you find this useful, or have any suggestions for improving this implementation.