MvcContrib now offers four (4) alternative view engines for ASP.NET MVC

That’s right.  If you prefer not to use the default ASPX view engine that comes with the ASP.NET MVC Framework, you now have options.  Here’s the rundown: (and keep tabs on my feed for more things MvcContrib: http://feeds.feedburner.com/jeffreypalermo)

The key here is options, and MvcContrib provides it.  I have to give credit to all the folks who have put time and energy into this project. 

The goal of this project is to be the community offering for extras for the ASP.NET MVC Framework.  If you are working on something that might be useful to someone else, consider contributing.  Check out the project at mvccontrib.org.  All good, documented, 95% unit test covered code with samples is welcome.

January Headspring training sold out: Opening two more dates

The January training schedule for the Headspring .Net Boot Camp is sold out, but we’ve opened the following dates.  All classes are only in Austin, TX:

Jan 30, Jan 31, and Feb 1
8:30 a.m. to 5:30 p.m.
SOLD OUT

Feb 20, Feb 21, and Feb 22
8:30 a.m. to 5:30 p.m.

March 12, March 13, March 14
8:30 a.m. to 5:30 p.m.

There is not a more advanced .Net training course in all of Austin, TX.  We apply the theoretical and make it practical, and students will learn the same techniques that Senior Consultants at Headspring Systems use on real software projects.  I am the trainer for all of these dates, so if you are already good at C# programming but want to take your practice to the next level, sign up for our Feb. or March class, and let us help you climb the skill ladder.

Warning:  This class is not for beginners.  This class is not for intermediates.  We will not be covering basic concepts of .Net programming.  We expect students to already be seasoned practitioners of .Net.  Our first day we’ll be dealing with advanced concepts, so if you are looking for more entry-level training, then these classes are not for you.

If your company would like on-site, customized training, please contact us:  1-877-459-2260

http://www.headspringsystems.com/training/

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

How to write stories _before_ the project kicks off

Recently, I was coaching a client who was embarking on a new project.  The project team was not formed, but key stakeholders were planning what the projected needed to be.  In Agile (specifically our Lean/Scrum/XP mix), we have iteration planning, release planning, and project planning.  This client is doing project planning, and the question came up about how to write stories for the project.  After all, we have to know the entire scope of the project in order to plan it, right?  Historical trends tell us that even if we think we know the full scope of the project, we’ll be wrong, so I helped this client identify the key components of the project to formulate the high-level project scope. 

If you like this post, you can subscribe to my feed at http://feeds.feedburner.com/jeffreypalermo

After we understood what the high-level requirements of the new software would be, we did a prioritization and created a backlog that we hoped would turn into a short-term first release.  With this rough release plan in mind, we did a small story-writing workshop.  We first wrote some stories together and then broke off into pairs to complete the remaining stories.  We concentrated on only the first release because that release contained the highest priority items (from the prioritized backlog).  We knew we were working on the highest value items first. 

Here is an example of a story (adapted from CodeCampServer as a realistic example):

"As an organizer I want to have a place to enter maps and driving
directions so that the attendees can find the event."

First, we have the persona of the organizer.  The organizer is in charge of the event and is doing the planning.  Often it’s helpful to give him a name.  We’ll call him Oscar the organizer.  Then, we can discuss what Oscar cares about to get a feel for how he’ll use the system. 

Your first thought is that this isn’t enough detail to formulate a release plan, and you’re right, but it’s a placeholder for a conversation with the team that’s going to make it happen.  I think a requirements document is also necessary.  Writing stories doesn’t take away the need to think about some level of detail while planning the overall project.  After all, when estimating the release, it’s necessary to know if driving directions merely means a link to Google Maps or if it means an interactive mapping system built into the application. 

Be careful about adding too much detail to the story because then people won’t read it.  If every story is a full page, it’s too long.  Story can act as headers in a requirements document, however, if you prefer to organize it that way.

Ok, buddy, but how will we know what the acceptance conditions are?  You need to know the high-level acceptance conditions, but I caution against laboring too much on small details of each story before the project has even kicked off.  You’ll paralyze yourself with analysis.  Define just enough detail to move to the next step, release planning.  By the time you move through iteration planning, and the team has asked all their questions, all the details will be fleshed out, and the team can turn the story into working software. 

Finally, get help from someone with Agile experience.  I’ve seen many people who have studies Lean/Scrum/XP struggle to move from use cases to stories with only academic knowledge.  There is no replacement for apprenticeship.

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.

About Me

Jeffrey Palermo is a Managing Partner & CEO of Clear Measure, Inc, a software engineering firm in Austin, TX.  At Clear Measure, Jeffrey manages the software engineering practice which includes new system development, legacy system upgrades, performance/scalability improvements and system rescues.  Previously, Jeffrey has held executive positions at various firms including President, COO, CIO, CTO, Chief Architect, and VP of Engineering.



Jeffrey has been recognized by Microsoft as a “Microsoft Most Valuable Professional” (MVP) each year since 2006.  He has spoken and facilitated at industry conferences such as VSLive, DevTeach, the Microsoft MVP Summit, and Microsoft Tech Ed. He also speaks to user groups around the country as part of the INETA Speakers’ Bureau. A graduate of Texas A&M University, an Eagle Scout, and an Iraq war veteran, Jeffrey has published many magazine articles, and he has written three editions of his book, ASP.NET MVC in Action.

Subscribe to Jeffrey’s blog feed here: http://feeds.jeffreypalermo.com/jeffreypalermo

Read about Clear Measure.

Austin Code Camp 2008 scheduled for May 17, 2008

The Austin .Net User Group is hosting its third annual code camp on May 17th, 2008.  Our past two code camps have been around 150 in size and contained several sessions running side-by-side.

This years code camp is going to have more than just presentations and will include and open space portion. 

Details are sparse at this time, but there will be more information to come as the date draws nearer.

Headspring welcomes Jimmy Bogard

I haven’t posted much about the goings-on of my company, Headspring Systems, but this is worth mentioning. 

Jimmy Bogard just joined us!  Yes, I’m excited.  Those in Austin know him from the Austin .Net User Group and AgileAustin, and others know him as one of the NBehave team members and blogger on LosTechies.  He attended the AltNetConf and leads book clubs in Austin, TX.  His current book club is studying Domain-Driven Design by Eric Evans.

Jimmy started yesterday, and the first day was like all first days – setting up the good old workstation.  We’re excited to have Jimmy on board.  He’ll definitely be an asset on the .Net consulting projects we do.

Jimmy’s post here.

Reading in config files _one_ way – what’s the path?

Executive summary:  AppDomain.CurrentDomain.SetupInformation.ApplicationBase

Subscribe here: http://feeds.feedburner.com/jeffreypalermo.

In a console app, this directory is where the assemblies are.  In ASP.NET, this directory is where the web.config file is, not the assemblies.  Other methods seem to be less reliable and rely on the current directory.  This can change whether the code is executing stand-alone or through Visual Studio.  the above API seems to be very consistent.

Please share your experiences.

CodeCampServer OSS project call for participation

Officially announcing a new open-source project, CodeCampServer.  As usual, you can subscribe to my RSS feed to keep up-to-date with more information about this here: http://feeds.feedburner.com/jeffreypalermo.  The mission of the first release is to provide an ASP.NET MVC Framework solution that can serve as the CodeCamp website for a user group.  The project will be able to track and manage multiple conferences for a single user group.  This project is a consumer of the MvcContrib project. 

At this point, we have a project workspace on GoogleCode, and we have a backlog.  Feel free to participate in the project’s discussion list and suggest stories.  We’ll add them to the backlog and prioritize appropriately.  We are intentionally focusing the release 1 in order to get some usable software out quickly.  We will be maintaining a healthy code base with full NUnit test coverage.  You will be able to track the project’s CI with CCTray by referencing the CCNet server on build.codecampserver.org soon (after I set it up).  We already have the NAnt automated build in place, but the domain isn’t set up yet.

Ben Scheirman is one of the lead developers on the project, and I’m serving as project coordinator and developer.  Eric Hexter has set up the CCNet build.  The easiest way to contribute is to grab a high-priority story off the backlog, communicate on the discussion list that you want to work on it, and then work toward submitting a patch.  If you are interested in becoming a committer, then please start by committing patches so we can get to know each other.

When you link to the project, please link to http://codecampserver.org/.