The keynote with Scott Gu just ended and I am sitting in the keynote hall waiting for the second keynote.

The most interesting points:

  • Visual studio 2010 will use WPF, the UI will not only look and work better but most of all will be easier to extend using MEF. Scott showed how to create a really nice visualization of method xml comments by extending the code editor, this was done implementing a very simple interface, exposing the class using the MEF Export attribute and drop the assembly in a components visual studio directory, no need to register components in the registry anymore.
  • Windows 7 will contain some actual usability improvements not only a new glass look :)
    • They showed it running on a netbook with one 1GB ram where there the OS used 512 MB, seems they will be optimising it for this scenario. 
  • Live Mesh seems interesting, Office 14 seems to use it a lot.
DSC00089

 

The second keynote with Don Box has begun and I better listen...

Updated with more pictures:

Picture 059

Picture 051

Pretty big room. Those gray rectangles hanging from the roof is projector screens for those in the back.

Picture 028

I am the guy in the black t-shirt.

Picture 045

Out last night at Tailors steak house, from left to right, Joakim  Sundén, Patrik Löwendahl, Magnus Mårtensson.

If you are running an ASP.NET MVC application under IIS6 you will need an extension in the URL in order for the request to be handled by the ASP.NET runtime. You can configure IIS so that all requests, no matter what extension, is handled by the aspnet_isapi filter but if you do this your application must handle content request as well (like css and image files). The best way to handle extensionless urls under IIS6 is to use Helicon Tech’s ISAPI_Rewrite 3. This isapi filter will rewrite the url before it is being processed, in effect it takes an extensionless url and rewrites it into an url with an extension. 

ISAPI_Rewrite is a commercial product, however there is a free lite version that works really well (it has some limitations). In ISAPI_Rewrite you write the rewrite rules using regular expressions:

RewriteEngine on
RewriteBase /

RewriteRule ^Home/(.*?)$ Home.mvc/$1 

Charles Vallance has written an excellent post on extensionless urls with ASP.NET MVC, the problem with his solution is that it requires that each route rule be duplicated, one with extension and one without.

routes.MapRoute(
  "Default", 
  "{controller}/{action}/{id}", 
  new { controller = "Home", action = "Index", id = "" } 
);

routes.MapRoute(
  "Default", 
  "{controller}.mvc/{action}/{id}", 
  new { controller = "Home", action = "Index", id = "" } 
);

The reason why you need two rules is that one is used for inbound urls (which after isapi_rewrite have an extension), the other is for outbound urls so that generated links from helpers are extensionless. This duplication of route rules might not be a big problem if you only use the default routing schema but if you use a lot of specific routes you want a better way to declare your routes so you do not need to duplicate them.

I have blogged previously about the route fluent interface I created for CodeSaga. I extended this further to handle this route duplication. In the url definition I only need to place a marker where the extension is going to be:

SagaRoute
  .MappUrl("admin$/repository/edit/{reposName}")
  .ToDefaultAction<RepositoryAdminController>(x => x.Edit(null))
  .AddWithName(RouteName.EditRepository, routes);

The actual route duplication is handled by the AddWithName function:

public SagaRoute AddWithName(string routeName, RouteCollection routes)
{
    var clone = this.Clone();
    Url = Url.Replace("$", "");

    if (ShouldAddExtensionlessRoute())
    {
        routes.Add(routeName, this);
        routes.Add(routeName + ".mvc", clone);
    }
    else
    {
        routes.Add(routeName, clone);
    }

    return this;
}

public SagaRoute Clone()
{
    var clone = new SagaRoute(Url.Replace("$", ".mvc"));

    foreach (var pair in Defaults)
        clone.Defaults.Add(pair.Key, pair.Value);

    foreach (var pair in Constraints)
        clone.Constraints.Add(pair.Key, pair.Value);

    return clone;
}

private bool ShouldAddExtensionlessRoute()
{
    return RuntimeContext.Config.UrlExtensionMode == UrlExtensionMode.WithoutExtension;
}

In the code above I clone the route, replace the dollar sign with ".mvc" and remove the dollar from the original route. The extensionless route must be added before the one with the extension, this is because it needs precedence when generating the outbound urls. There is also a setting that controls if the extensionless route is added at all, this is for IIS6 users that don't want to bother with isapi_rewrite. 

I hope this comes in handy if you are working on an ASP.NET MVC application that needs to support IIS6 and IIS7 in extension and extenionless modes.

If you are new to MVC web development it can initially be tricky to figure out how to handle UI features that will be used by multiple views. In WebForms you would simply create a control that encapsulated this element's look and function. In CodeSaga there are many views that share view elements, for example many views have tabs. In order to handle the common view elements I organized the hard typed view models into an inheritance hierarchy.

image

The above class diagram shows a subset of the view models in CodeSaga. The ViewModelBase exposes a list of MenuTabs and a method AddTabs that inheritors can use to add menu tabs. Here is the code for the RepositoryContextViewModel, the base class for all views that have the history, browse, search and authors tabs.

public class RepositoryContextViewModel : ViewModelBase
{
    public RepositoryUrlContext UrlContext { get; set; }
    
    public RepositoryContextViewModel()
    {
      AddTabs(
        MenuTab
          .WithName(MenuTabName.History)
          .ToAction<HistoryController>(x => x.ViewHistory("", null, null)),
        MenuTab
          .WithName(MenuTabName.Browse)
          .ToAction<HistoryController>(x => x.ViewBrowse("")),
        MenuTab
          .WithName(MenuTabName.Search)
          .ToAction<SearchController>(x => x.Search("")),
        MenuTab
          .WithName(MenuTabName.Charts)
          .ToAction<ChartsController>(x => x.ViewChart("")),
        MenuTab
          .WithName(MenuTabName.Authors)
          .ToAction<AuthorsController>(x => x.ViewStats("")));
    }    
}

The AdminViewBase class defines the admin tabs in a similar way. The view code that then renders the tabs is very simple:

<ul class="menu">
  <for each="var tab in tabs">        
    <li class="on?{tab.IsActive}">
      ${Html.ActionLink(tab.Text, tab.Action, tab.Controller)}
    </li>
  </for>        
</ul>

image

The only job left to do in the controller action is to set the currently active tab, like this:

public ActionResult ViewDiff(string urlPath, int? r1, int? r2)
{
  var urlContext = RepositoryUrlContext.FromString(urlPath);
      
  var diff = repository.GetFileDiff(urlContext.ReposName, urlContext.Path, r1.Value, r2.Value);

  var model = new DiffViewModel
  {
    UrlContext = urlContext,
    FileDiff = diff,
    ActiveTabName = MenuTabName.History
  };

  return View("Diff", model);
}

Setting the active tab like this could be refactored to be handled in a more declarative way, for example with an attribute on the controller class or action method. But I haven't found the need to do that yet as I think it's pretty declarative as it is.

One can question why I have used a MenuTab presentation model at all, why not define the tabs directly in the views? Since the tabs are only declared statically you could achieve a similar result using a hierarchy of partial views. The reason I did not choose that solution is because I actually needed (or wanted) the solution to support creating tabs dynamically in an easy way. This is used in CodeSaga when you click the edit link in the repository list (in the admin), this will open the edit view in a new tab.

image

The controller action for this:

private ActionResult Edit(string reposName)
{
    var model= new RepositoryEditViewModel();
    model.Repository = reposRepository.GetByName(name);
    
    model.AddTabs(
      MenuTab
        .WithName("Edit " + reposName)
        .ToAction<RepositoryAdminController>(x => x.Edit(reposName))
        .SetActive());
        
    return View(model);
}

It is worth to point out that the term View Model in this post should not be confused with Presentation Model. Instead I see it as a sort of container data structure for all the domain and presentation model data a specific view need.

I found a good use for the InlineData xUnit attribute:

public class DateDiffCalculatorFacts
{
    [Theory,
    InlineData("2008-09-25 14:31", "1 minute"),
    InlineData("2008-09-25 15:05", "35 minutes"),
    InlineData("2008-09-25 16:05", "1 hour 35 minutes"),
    InlineData("2008-09-25 17:05", "2 hours 35 minutes"),
    InlineData("2008-09-25 19:05", "4 hours"),
    InlineData("2008-09-25 02:30", "12 hours"),
    InlineData("2008-09-24 00:00", "1 day 14 hours"),
    InlineData("2008-09-23 00:00", "2 days 14 hours"),
    InlineData("2008-09-20 00:00", "5 days"),
    InlineData("2008-08-20 00:00", "1 month 5 days"),
    InlineData("2008-07-01 00:00", "2 months 24 days"),
    InlineData("2008-05-01 00:00", "4 months"),
    InlineData("2007-07-20 00:00", "1 year 2 months"),
    InlineData("2005-07-20 00:00", "3 years")]
    public void Can_get_correct_age_for_date(string dateString, string expectedAge)
    {
        var date = DateTime.Parse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.None);
        var referenceDate = DateTime.Parse("2008-09-25 14:30:00", CultureInfo.InvariantCulture, DateTimeStyles.None);

        var calc = new DateDiffCalculator(date, referenceDate);

        string age = calc.ToString();
        Assert.Equal(expectedAge, age);
    }
}

This is the first time I have used this style of testing (called RowTests in MbUnit/NUnit). Maybe in this case it is not the right thing to do from a pure TDD perspective as the test name is not very descriptive of what is being tested. If each InlineData was extracted to a sepereate test they could be given more meaningfull names like "Measure_will_be_in_plural_when_more_than_one" and "Skip_sub_measure_when_main_measure_is_higher_than_two". On the otherhand having the test like above made it dead simple and fast to add new test cases and it still quite apparenent what the intended outcome is.

image There are currently 188 sessions for this years PDC and they are still adding new sessions! I am one of the lucky ones that will be going and I can't wait, there are so many interesting sessions and after conference parties :)

Here are some sessions I am looking forward to:

  • The Future of C# - Anders Hejlsberg
  • Deep Dive: Dynamic Languages in Microsoft .NET - Jim Hugunin
  • IronRuby: The Right Language for the Right Job - John Lam
  • Microsoft .NET Framework: CLR Futures
  • Managed Extensibility Framework: Overview - Glenn Block
  • Under the Hood: Advances in the .NET Type System

I have high hopes for Anders Hejlsberg talk on the future of C#, I remember his talk at PDC 2005 where he presented LINQ,  who knows what cool stuff he might unveil this time.

Many of the sessions cover Oslo, I am not that interested in Oslo, especially after the information that has been released about Oslo describing it as "Microsoft's data-centric platform, which is aimed at empowering nondevelopers to build distributed applications". There are also a ton of sessions on Windows 7, Silverlight and Visual Studio.

Anyway, I hope to meet some fellow bloggers and .NET enthusiasts there :)

image I just completed Robert C. Martin latest book Clean Code "A Handbook of Agile Software Craftsmanship", which was a good read but far from as good as his Agile Principles, Patterns and Practices book which I rank among my favorite programming books. If you haven't read it stop reading this and go read it! No just kidding stay..  

Clean Code begins with an interesting chapter where the concept of clean code is defined. The chapter includes about a dozen of quotes from famous programmers who are asked to define what clean code means to them. They were all good but I especially liked Michael Feathers definition (author of working effectively with legacy code):

/.../ Clean code always looks like it was written by someone who cares. /.../

I think this is very true, if you really care about the code you write you won't leave it in a messy state but continue to refactor it until your are satisfied.

So why clean code? Well Martin argues, correctly, that the time spent reading vs writing code is very high, and that we are constantly reading old code in order to write new code. So making it easier to read will make it easier to write.

image

So how do you write clean code? Well that is what the next chapters cover, for example meaningful names, small functions,  formatting, unit tests, etc. There are a lot of code in some of these chapters, and I mean a lot. So it can be pretty tedious at times. Robert tries to show step by step how he improves some existing pieces of real code but I found it hard to really follow exactly what he was doing as the code pages were so long and there was no color highlight to mark changed lines or syntax.

I think Robert should consider doing screencasts on writing clean code or on doing TDD, it is a medium that is better suited to showing how you in small steps evolve and change code. These could then later be included on CDs with his books.

Anyway despite it's problems Clean Code is still a good read. I did learn a few new tricks and will be more strict on keeping my functions shorter :)

Dam, writing installation & documentation it is boring. I never expected it to be this much work to just get a first release out the door. Well now that it is done I can spend time implementing new features again.

Yesterday I finished the implementation of the history log rss feed. I was also able to fix CSS issues with Firefox2 and Chrome as well. There is a new download available on www.codesaga.com, the demo site is also updated.

The rss feed was interesting to implement as it was the first time I ever implemented an rss feed. It was very easy, just a normal view but with the response content type set to text/xml.

<?xml version="1.0"?>
<viewdata model="HistoryRssViewModel" />
<viewdata urlContext="RepositoryUrlContext"/>
<rss version="2.0">
    <channel>
        <title>CodeSaga - ${Html.Encode(urlContext.ReposName)}</title>
        <link>
            ${Url.AbsoluteRouteUrl(RouteName.History, new {urlPath=urlContext.Url})}
        </link>
        <description>
            History for the ${Html.Encode(urlContext.ReposName)} repository and directory ${Html.Encode(urlContext.Path)}            
        </description>

        <item each="var changeset in ViewData.Model.Changesets">
            <var changesetUrl="Url.AbsoluteRouteUrl(RouteName.ChangesetDetail, new {urlPath=urlContext.Url, cs=changeset.Revision})" />
            
            <title>${Html.Encode(changeset.Message.TrimWithElipsis(90))}</title>
            <pubDate>${changeset.Time.ToString("R")}</pubDate>
            <author>${Html.Encode(changeset.Author)}</author>    
            <link>${changesetUrl}</link>
            <guid isPermaLink="false">${changesetUrl}</guid>
            
            <description>
                /// rss article html
            </description>                                    
        </item>        
    </channel>
</rss>

I omitted the rss item content to keep the sample smaller. The biggest change from a normal view is that all links and urls must be made absolute. For this I created a extension method, AbsoluteRouteUrl, on the UrlHelper class.

Since you cannot include css files in the item description you are limited to inline css, and most rss readers will parse the rss item html and remove unwanted tags and css. I tried a simple table layout with cell background colors, it looks pretty good in Newsgator:

image

I also tried Google Reader, it looks similar but not as good in google reader as it forces a specific width for the rss item content, not good for wide monitors (you can override this by using a greasemonkey script).

I haven't figured out a good way to unit test the rss feed yet. I am not talking about the controller action, that was simple, no I mean the actual rss output generated by the view. One way would be to do some serious mocking to get the view engine to work in a unit test or try Watin and see how it handles rss content.

I have a lot of fun stuff in the backlog for CodeSaga:

  • Interactive charts and graphs in Silverlight (why not I am doing this app for fun and this sounds like fun to do!)
  • Advanced search options and filtering, search on file content using Lucene.NET
  • Arbitrary diffs, side by side diffs, diff options (context lines)
  • File content view,
  • File history view 
  • Integration with TFS issue tracking
  • Parsing commit message for issue ids and link to issue

Will all this get done? I highly doubt it :) But it is good to have a plan / vision. How come I have had time to work on this? Well I haven't been coding much at work the last couple months so I have had a lot of will / energy to work on something after work and while hung over on Sundays.

And to be honest it just began as a experiment and grew to something more, and the last couple of weeks it has been like "well you have spent all this time working on this, you might as well release it and not let all that time go to waste". Not that it would have been a waste, I feel like I am getting very efficient with ASP.NET MVC. It feels kind of daunting now that it has been released, because now I feel even more obliged to work on it.

CodeSaga is a source code repository web analysis app, very similar to FishEye. I have been working on this as a hobby project for a while now. It basically grew out of frustration with the existing TFS web tools and the need to have something fun to work on.

History log with an expanded diff:

image

Start page:

image

For more screenshots, feature descriptions and download info: www.codesaga.com, there is also live demo site for it, have a look: demo.codesaga.com

I will do a more detailed blog post later, I am to very tired right now, I have been working all day on installation & admin documentation plus fixing some critical issues before this first release.

I really like the explorer address bar in Vista, it is sort of like a breadcrumb but with menu drop downs:

image

In the application I am working on I wanted to create something similar, a mix between a breadcrumb and a menu. I wanted the menu "dropdown" to be filled with an ajax call, and I wanted the solution to be general enough to be easy to use in all parts of the application and dynamic so the breadcrumb/dropdown can be populated with what ever.

Here is a screenshot of what I ended up with:

image

In order to handle the ajax call and the dropdown javascript logic in a general way that would work over the whole application I ended up with what I think is an interesting solution, here is an example of what the html looks like for the breadcrumb:

<div id="breadcrumb">
  <a href="/History">History</a>
  <span class="dropdown {ajaxUrl: '/History/GetDirectories'}" />
  <a href="/History/trunk">trunk</a>  
  <span class="dropdown {ajaxUrl: '/History/GetDirectories/trunk'}" />
</div>

The span with the dropdown css class is the arrow element that you click on to show the dropdown menu. So what is the strange ajaxUrl that is embedded in the class attribute? The ajaxUrl is metadata that can be retrieved using the JQuery metadata plugin. This plugin allows you to place metadata in json format inside the class attributes, the data can then be easily extracted as a javascript object by just calling the metadata method.

The point of all this is that it is now very easy for each view to specify a unique breadcrumb and ajax url where the dropdown html content will be retrieved from. This is how it is used from the home view (using spark view engine):

<content:breadcrumb>
    ${Html.ActionLink("Home", "Index")}    
    <var ajaxUrl="Url.Action('GetRepositories', 'Home')" />    
    <span class="dropdown {ajaxUrl: '${ajaxUrl}'}"></span>
</content:breadcrumb>

The "<content:breadcrum" syntax is a way to output to a region in the master view (like content placeholders in the WebForms view engine).  In the above example the ajaxUrl is configured to route to the controller action GetRepositories, this action will render a partial view with what every should appear in the dropdown menu, in this case a list of repositories in a <ul> list.

image

Here is the javascript jquery code that performs the ajax call:

$(".dropdown").click(function(evt) {
  $(dropDown).toggleClass("expanded");    
  
  $("#breadcrumb-dropmenu")
    .hide()
    .html("<div class='ajax-loader-white'></div>")
    .css( { left: evt.pageX + "px" })            
    .slideDown("fast");    
    
  evt.stopPropagation();
  
  var ajaxUrl = $(this).metadata().ajaxUrl;        
  
  $.ajax({
    type: "GET",
    url: ajaxUrl,
    dataType: "html",
    success: function (data) {
      Breadcrum_GetChildren_AjaxCallback(data);
    }
  });
  
});

It is interesting how the MVC programming model forces you to rethink old problems and come up with new solutions. Implementing a breadcrumb like this in WebForms would require a very different solution.

I guess no one has missed has the news that JQuery will ship with future versions of the ASP.NET MVC framework and eventually with Visual Studio. Ayende summed up my feelings best: "shocked and thrilled"! This not something I expected, I expected a Microsoft ASP.NET AJAX Query Framework or something :) This is the first time that I can think of that Microsoft will bundle a piece of open source software with one of their products. I am very excited by this new development from Microsoft.