SmartBag for ASP.NET MVC, take two

Earlier, I announced the advent of the SmartBag for the ASP.NET MVC Framework.  Quite a few folks commented on it, and it has been improved as a result of the comments.  For more from me, add my feed at http://feeds.feedburner.com/jeffreypalermo.

The main problem with the SmartBag prior to today was that they actual type of the object passed was used as the key.  This created a problem for polymorphism because proxied objects would be recorded as the actual type, and then when the view asked for them by the intended type, the object would not be found.  I fixed this by having the Add method be generic and accept the intended type.  This still looks pretty clean, allows multiple initialization, and supports proxied and subclassed objects.  The full source code is still only in CodeCampServer, so you can swipe it from there.

Rather than address every capability, I think the tests best illustrate the current capabilities.  If you’d like to see real code using the SmartBag, every controller and view in CodeCampServer are currently employing it.  I’m really close to moving it to MvcContrib and providing a sample there so that it can be used more generically as well as be integrated into ConventionController.  I really think this ends up being a nicer experience both on the controller and the view.

   1: [Test]
   2: public void ShouldRetrieveSingleObjectByType()
   3: {
   4:     SmartBag bag = new SmartBag();
   5:     Url url = new Url("/asdf"); //arbitrary object
   6:     bag.Add(url);
   7:  
   8:     Assert.That(bag.Get<Url>(), Is.EqualTo(url));
   9:     Assert.That(bag.Get(typeof (Url)), Is.EqualTo(url));
  10: }
  11:  
  12: [Test, ExpectedException(ExceptionType = typeof (ArgumentException),
  13:     ExpectedMessage = "You can only add one default object for type 'System.Security.Policy.Url'.")]
  14: public void AddingTwoDefaultObjectsOfSameTypeThrows()
  15: {
  16:     Url url1 = new Url("/1");
  17:     Url url2 = new Url("/2");
  18:  
  19:     SmartBag bag = new SmartBag();
  20:     bag.Add(url1);
  21:     bag.Add(url2);
  22: }
  23:  
  24: [Test, ExpectedException(typeof (ArgumentException),
  25:     ExpectedMessage = "No object exists with key 'System.Security.Policy.Url'.")]
  26: public void ShouldGetMeaningfulExceptionIfObjectDoesntExist()
  27: {
  28:     SmartBag bag = new SmartBag();
  29:     Url url = bag.Get<Url>();
  30: }
  31:  
  32: [Test]
  33: public void ShouldReportContainsCorrectly()
  34: {
  35:     SmartBag bag = new SmartBag();
  36:     bag.Add(new Url("/2"));
  37:  
  38:     Assert.That(bag.Contains<Url>());
  39:     Assert.That(bag.Contains(typeof (Url)));
  40: }
  41:  
  42: [Test]
  43: public void ShouldManageMoreThanOneObjectPerType()
  44: {
  45:     SmartBag bag = new SmartBag();
  46:     bag.Add("key1", new Url("/1"));
  47:     bag.Add("key2", new Url("/2"));
  48:  
  49:     Assert.That(bag.Get<Url>("key1").Value, Is.EqualTo("/1"));
  50:     Assert.That(bag.Get<Url>("key2").Value, Is.EqualTo("/2"));
  51: }
  52:  
  53: [Test, ExpectedException(typeof (ArgumentException), 
  54:     ExpectedMessage = "No object exists with key 'foobar'.")]
  55: public void ShouldGetMeaningfulExceptionIfObjectDoesntExistByKey()
  56: {
  57:     SmartBag bag = new SmartBag();
  58:     Url url = bag.Get<Url>("foobar");
  59: }
  60:  
  61: [Test]
  62: public void ShouldCountNumberOfObjectsOfGivenType()
  63: {
  64:     SmartBag bag = new SmartBag();
  65:     Assert.That(bag.GetCount(typeof (Url)), Is.EqualTo(0));
  66:  
  67:     bag.Add("1", new Url("/1"));
  68:     bag.Add("2", new Url("/2"));
  69:     bag.Add("3", new Url("/3"));
  70:  
  71:     Assert.That(bag.GetCount(typeof (Url)), Is.EqualTo(3));
  72: }
  73:  
  74: [Test]
  75: public void ShouldBeAbleToInitializeBagWithSeveralObjects()
  76: {
  77:     Url url = new Url("/1");
  78:     GenericIdentity identity = new GenericIdentity("name");
  79:  
  80:     SmartBag bag = new SmartBag().Add(identity).Add(url);
  81:     Assert.That(bag.Get(typeof (GenericIdentity)), Is.EqualTo(identity));
  82:     Assert.That(bag.Get(typeof (Url)), Is.EqualTo(url));
  83: }
  84:  
  85: [Test]
  86: public void ShouldHandleProxiedObjectsByType()
  87: {
  88:     MailMessage stub = MockRepository.GenerateStub<MailMessage>();
  89:     SmartBag bag = new SmartBag();
  90:     bag.Add(stub);
  91:     MailMessage message = bag.Get<MailMessage>();
  92:  
  93:     Assert.That(message, Is.EqualTo(stub));
  94: }
  95:  
  96: [Test]
  97: public void ShouldInitializeWithProxiesAndResolveCorrectly()
  98: {
  99:     MailMessage messageProxy = MockRepository.GenerateStub<MailMessage>();
 100:     XmlDocument xmlDocumentProxy = MockRepository.GenerateStub<XmlDocument>();
 101:  
 102:     SmartBag bag = new SmartBag().Add(messageProxy).Add(xmlDocumentProxy);
 103:  
 104:     Assert.That(bag.Get<MailMessage>(), Is.EqualTo(messageProxy));
 105:     Assert.That(bag.Get<XmlDocument>(), Is.EqualTo(xmlDocumentProxy));
 106: }
 107:  
 108: [Test]
 109: public void ShouldInitializeWithKeys()
 110: {
 111:     SmartBag bag = new SmartBag().Add("key1", 2).Add("key2", 3);
 112:     Assert.That(bag.ContainsKey("key1"));
 113:     Assert.That(bag.ContainsKey("key2"));
 114: }