NAnt 0.86 beta 1 nightly build seamlessly supports .Net 3.5 and a host of other platforms.

To subscribe to my feed, add the following Url:  http://feeds.feedburner.com/jeffreypalermo.

With the original 0.86 beta 1 release, there was a small problem with folks who only had VS 2008 installed because there was a dependency on a registry key that was installed with the .Net 2.0 SDK (or VS 2005). 

The latest nightly build has fixed this, and a clean machine with VS 2008 can now use NAnt just fine.  CodeCampServer is using NAnt 0.86.2909.0, and it’s working well so far. 

I have to say that over the past 2.5 years, I have been very pleased with NAnt.  It is a great open-source build tool, and it integrates with every other command-line tool I could want.  I normally <exec/> out to msbuild.exe for the compile.   I have a NAnt build template available in my public source code repository here.

Use caution with anonymous types and other new C# language features

To subscribe to my feed, add the following Url:  http://feeds.feedburner.com/jeffreypalermo.

I think Microsoft is making a huge mistake by leaning so heavily on anonymous types as API parameters, particularly in the ASP.NET MVC Framework.  Not only does this make the APIs in the system ugly and error-prone, but it’s not intuitive, and there is no intellisense for it because the API is untyped (typed as object) in the case where an anonymous type is expected.  I think we should stick with named types and named methods wherever possible.  I currently don’t see a problem with lamda’s if they give an advantage over regular arguments. I’m very cautious about automatically using new, unproven language features before we fully understand how they will affect the usability and maintainability of code.  Note:  we have NO track record for the maintainability of these new language features, and they were developed specifically for LINQ, so using them outside of that context is suspect, in my opinion.

I do have a conservative disposition on this, but this is through learning from the past.  I still remeber OpenXML is SQL Server 2000: how so many developers automatically converted stored procedure interfaces to be a single XML string rather than individual arguments.  We need to fully understand the long-term strengths and weaknesses of features in order to maximize the value of usage or non-usage.

Party with Palermo : MVP Summit 2008 edition – save the date

The time is upon us again.  Announcing Party with Palermo: MVP Summit 2008 edition on April 13th, 2008 from 7pm – 10pm in Seattle, WA. 

The last PwP at the MVP Summit in 2007 saw 235 wild and rowdy developers having a great time.

At this time, the registration website is not ready, but details will be coming soon.  Check back frequently, and subscribe to the party feed at http://feeds.feedburner.com/partywithpalermo.

If you or your company is interested in sponsoring, please contact me at jeffrey [at]_ palermo d0t cc.

The purpose of this event is completely social and a fun way to kick off the week.  Anyone is invited, and the only cover charge is a single business card to keep our sponsors happy.

Code to Live video cast available on ASP.NET MVC from Tulsa TechFest

At theTulsa TechFestt back in October, Chris Koenig filmed me and Nick Parker having a discussion about the ASP.NET MVC Framework.  At that point, the only public information was what Scott Guthrie shared at the AltNetConf.  Even so, there was a lot to talk about.  For more on ASP.NET MVC, subscribe to my feed at http://feeds.feedburner.com/jeffreypalermo

Watch here:  http://channel9.msdn.com/ShowPost.aspx?PostID=375432

First Headspring .Net Boot Camp wrap-up

HeadspringTraining20080130Yesterday concluded the first training course put on by Headspring Systems (and taught by me).  A non-biased write-up can be found here by Karthik Hariharan of Telligent.

The course curriculum was completely hands-on and built around a real Work Order system with an automated build and automated unit and integration tests.  The code is available to the public via the Google Code subversion repository.  You can update to a different revision committed each day.  We ran a local SVN repository to have fast network connectivity, but I pushed a commit to the Google Code repository along the way.  You are free to see how the system progressed over the three days.  The system is not meant to be a complete example of everything right.  There are things to correct intentionally put in as a learning tool for the class; however, you can take that system and adapt it to your needs. 

The course covered the following topics, tools, and design patterns:

  • Performance optimization
  • Subversion
  • Onion architecture
  • Inversion of Control
  • Domain Driven-Design
  • Test-Driven Development
  • SVN branching and merging
  • Resharper
  • Build automation with Nant and CCNet
  • Pair programming & the TDD game
  • Dynamic & static mocks & stubs
  • Refactoring
  • Integration testing
  • Interfaced-based programming
  • Team dynamics
  • NHibernate basics
  • Using CruiseControl.net
  • Source control structure (trunk, tags, branches)
  • CCTray
  • Team design/whiteboard modeling
  • Optimizing NHibernate queries
  • Automated database migrations
  • Complex NHibernate mappings
  • Automated deployments
  • Nant
  • Redgate SQL Compare
  • SQL Profiler
  • TortoiseSVN
  • NUnit
  • VisualSVN
  • TestDriven.Net
  • Log4Net
  • RhinoMocks
  • Separation of Concerns
  • Factory pattern
  • Model-view-presenter
  • Strategy pattern
  • Builder pattern
  • Repository pattern
  • Aggregate pattern
  • Template method
  • Visitor pattern
  • Specification pattern
  • Dependency Injection pattern

Future course date are available here.  You can see from the list above that we cover many of the practices of Extreme Programming.  What you don’t see are things that you can learn at other training courses, like LINQ, etc..  Our goal is to provide a unique training course that isn’t available elsewhere.  If you are interested in learning C# specifically or .Net programming, I’d also recommend JP Boodhoo’s Nothin’ but .Net series.

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.