CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo [MVP]

Software management consultant and CTO, Headspring Systems

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: }


Comments

Ken Egozi said:

I like the tests, I don't like the intent they reveal.

why use the type as key?

I like using the DictionaryAdapter and an interface-per-view approach much better. could make testing easier, too.

# January 25, 2008 3:28 AM

Introducing the SmartBag for ASP.NET MVC. . . and soliciting feedback - Jeffrey Palermo [MVP] said:

Pingback from  Introducing the SmartBag for ASP.NET MVC. . . and soliciting feedback - Jeffrey Palermo [MVP]

# January 25, 2008 8:09 AM

Jeffrey Palermo said:

@Ken,

I use the type as the key in order to provide a way to pull out the default object of a given type.  The generic usage eliminates the need for casting, and I believe we have an 80% solution here.

Please elaborate on what you like about DictionaryAdapter and what you gain by defining an interface for each view.

# January 25, 2008 8:14 AM

Jeffrey Palermo said:

@Ken,

The SmartBag is definitely not compatible with DictionaryAdapter, but it makes it easy to work in the view.

I think another capability that MvcContrib should provide is a ViewBase class that leverage's Castle's DictionaryAdapter.  

My thinking behind this is that I see value in both approaches and that I don't think the world (or MvcContrib users) will all use the same approach.

I read up on your blog about AspView and Dictionary Adapter.

# January 25, 2008 8:27 AM

Greg said:

I am morbidly curious how this test actually works ...

[Test]

public void ShouldHandleProxiedObjectsByType() {

    MailMessage stub = MockRepository.GenerateStub<MailMessage>();

    SmartBag bag = new SmartBag();

    bag.Add(stub);

    MailMessage message = bag.Get<MailMessage>();

}

to add a proxied object shouldn't I need to say bad.Add<MailMessage>(someProxiedObject)

 93:     Assert.That(message, Is.EqualTo(stub));

 94: }

# January 25, 2008 12:39 PM

Greg said:

ugh yucky type inference ...

# January 25, 2008 1:26 PM

Jeffrey Palermo said:

@Greg,

If I'm still not addressing your concern, please post a unit test that illustrates your desired behavior.  OR, submit a patch to CodeCampServer that fixes SmartBag to your liking.  It would help me understand your need.

# January 25, 2008 2:10 PM

Garry Shutler said:

I've put together my own version of a SmartBag which is tailored more to my liking to add to the conversation.

garryshutler.spaces.live.com/.../cns!63AE3374C229159C!152.entry

# January 25, 2008 5:04 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# February 5, 2008 10:43 PM

theQueue said:

Resources from my ASP.NET MVC talk at RockNUG (2/13/08)

# February 14, 2008 9:15 PM

Steve Sanderson’s blog » Blog Archive » ASP.NET MVC: Making strongly-typed ViewPages more easily said:

Pingback from  Steve Sanderson&#8217;s blog  &raquo; Blog Archive   &raquo; ASP.NET MVC: Making strongly-typed ViewPages more easily

# February 22, 2008 3:44 AM

About Jeffrey Palermo

Jeffrey Palermo is a software management consultant and the CTO of Headspring Systems in Austin, TX. Jeffrey specializes in Agile coaching and helps companies double the productivity of software teams. Jeffrey is an MCSD.Net , Microsoft MVP, Certified Scrummaster, Austin .Net User Group leader, AgileAustin board member, INETA speaker, INETA Membership Mentor, Christian, husband, father, motorcyclist, Eagle Scout, U.S. Army Veteran, and Texas A&M University graduate. Check out Devlicio.us!

This Blog

Syndication