Introducing the SmartBag for ASP.NET MVC. . . and soliciting feedback

To get objects from an ASP.NET MVC controller to a view, you put the objects in a dictionary of sorts called ViewData.  ViewData is sort of a misnomer because it’s not meant to contain data at all.  It’s meant to contain objects.   By the way, if you like this post, you can subscribe to my RSS at http://feeds.feedburner.com/jeffreypalermo.

There are some challenges with ViewData as it stands now:

  • A key is required for every object, both in the controller and view.
    ViewData.Add(“conference”, conference);
  • A cast is required to pull out object by key.
    (ScheduledConference)ViewData[“conference”]
  • The ViewPage<T> solution discards the valuable flexibility of the objectbag being passed to the view.
    <%=ViewData.DaysUntilStart.ToString() %> where ViewData is of type ScheduledConference

Facts (or my strong opinions):

  • Repeated keys from controller and view increase chance for typos and runtime errors.
  • Casting every extraction of an object in the view is annoying.
  • Strong-typing ViewPage only works for trivial scenarios.
    – For instance, suppose once logged in, every view will need the currently logged in user.  Perhaps the user name is displayed at the top right of the screen in the layout (master page).  Since the layout shares the viewdata with the page, we immediately have the need for a flexible container that supports multiple objects.  A strongly typed ViewPage<T> won’t work without an elaborate hierarchy of presentation object that are themselves flexible object containers able to support everything needed.  Once you get there, you are almost back to the initial dictionary.

My proposed draft solution, the SmartBag:  (and a follow-up here)

  • The SmartBag is currently only in CodeCampServer, but when it has proven it’s worth, I’ll move it into MvcContrib.  It’s used in the ConferenceController and the ShowSchedule view.  You can check out CodeCampServer at http://codecampserver.org.
  • public class SmartBag : Hashtable
    {
    public T Get<T>()
    {
    return (T)Get(typeof(T));
    }

    public object Get(Type type)
    {
    if (!ContainsKey(type))
    {
    string message = string.Format("No object exists that is of type '{0}'.", type);
    throw new ArgumentException(message);
    }

    return this[type];
    }

    public void Add(object anObject)
    {
    Type type = anObject.GetType();
    if (ContainsKey(type))
    {
    string message = string.Format("You can only add one default object for type '{0}'.", type);
    throw new ArgumentException(message);
    }

    Add(type, anObject);
    }

    public bool Contains<T>()
    {
    return ContainsKey(typeof (T));
    }
  • The smartbag implements IDictionary through HashTable, so it’s flexible, just like ViewData, but it has a powerful convention:  If you are passing one object/type to the view, we don’t need a key because the SmartBag can use the type as the key.  If you really need a Top conference and a Bottom conference, then you are back to using your keys, but for 80% (statistic I pulled out of my behind) cases, this works.
  • If we introduce a layer supertype for our views, then we can just use it:
  • public class ViewBase : ViewPage<SmartBag>
    {

    }
  • Then, all my views inherit from ViewPage<SmartBag>.

I’d appreciate feedback on this solution.  To me, it seems to be a good foundation and convention, but I’m sure it can be improved.  If this solution hasn’t been proven to be worthless, I’ll port it to MvcContrib so it can be used by MvcContrib clients.