May 2008 Entries

.NET 3.5 SP1 Beta and Its Effect on MVC

The news is out, the beta for the Visual Studio 2008 and the .NET Framework 3.5 Service Pack has been released. As it relates to ASP.NET MVC, there are two important points to notice about the SP1 release:

  • ASP.NET MVC is not included
  • URL Routing is included

Now you can see why there’s been so much focus on Routing from the MVC team, as Routing is now part of the Framework and is not out-of-band. This meant that we had to put a lot more effort into Routing to make sure it was production ready.

ASP.NET MVC continues to be an out-of-band release. With the Routing code now effectively complete, you should hopefully start to see a lot more progress in MVC as our development team can focus almost exclusively on MVC.

MVC Preview 2 Apps Are Affected By SP1

Another important thing to note is that installing SP1 Beta installs the System.Web.Routing and System.Web.Abstractions assemblies into the GAC (Global Assembly Cache). These assemblies have the same exact assembly version as the ones we released as part of Preview 2.

What this means is that even though you might have a direct reference to the Preview 2 assemblies, when your app runs, it will still load those assemblies from the GAC.

We’ve posted the workaround for Preview 2 applications at the bottom of the page here in the section entitled Changes for ASP.NET MVC Preview 2.

April CodePlex Apps

If you’re running the April CodePlex app, you should be okay using the version from SP1 Beta. There might be minor behavioral differences in edge cases.

Preview 3

MVC Preview 3 will not be affected by having SP1 installed. Preview 3 will include newer privately versioned bin deployable builds of the Routing and Abstractions DLLs. That way these assemblies will not be affected by the versions in the GAC installed by SP1, because they will have different version numbers.

They don’t call it “Bleeding Edge” for nothing when playing with these previews, but we are sorry for the inconvenience and Preview 3 should make it better.

Technorati Tags: ,,

Delegating Action Result

In my last post, I walked through a simple example of an ActionResult that you can use to transmit a file to the user’s browser along with a download prompt.

The MVC framework will include several useful action results for common tasks. However, we might not cover all results you might want to return. In this post, I walk through a simple result that will cover all remaining cases. With the DelegatingResult, you simply pass it a delegate. This provides ultimate control. Let’s see it in action.

public ActionResult Hello() {
  return new DelegatingResult(context => {
    context.HttpContext.Response.AddHeader("something", "something");
    context.HttpContext.Response.Write("Hello World!");
  });
}

Notice that we pass in a lambda to the constructor of the action result. This lambda is a delegate of type Action<ControllerContext>. By doing this, the lines of code within that block (Response.AddHeader and Response.Write) are deferred till later.

Here’s the code for this action result.

public class DelegatingResult : ActionResult {
    
  public Action<ControllerContext> Command {
    get;
    private set;
  }
    
  public DelegatingResult(Action<ControllerContext> command) {
    this.Command = command;
  }

  public override void ExecuteResult(ControllerContext context) {
    if (context == null) {
      throw new ArgumentNullException("context");
    }
        
    Command(context);
  }
}

I updated the sample I wrote in my last post to include this demo. Download the source.

Technorati Tags: ,,

Writing A Custom File Download Action Result For ASP.NET MVC

UPDATE: I’ve updated the sample to include a new lambda based action result. This also fixes an issue with the original download in which I included the wrong assembly.

The April CodePlex source drop of ASP.NET MVC introduces the concept of returning an ActionResult instance from action methods. ScottGu wrote about this change on his blog.

In this post, I’ll walk through building a custom action result for downloading files. As you’ll see, they are extremely easy to build. Let’s start at the end and see what the end-user behavior of this new result will be.

Here's a page that contains a link to an action method named Download. This action method returns this new DownloadResult action result.

File Download HomePage

Clicking on the link then pops up this dialog, prompting you to download and save the file.

File Download

The code for this action is pretty simple.

public ActionResult Download() 
{
  return new DownloadResult 
    { VirtualPath="~/content/site.css", FileDownloadName = "TheSiteCss.css" };
}

Notice that you just need to give the result two pieces of information, the virtual path to the file to send to the browser and the default filename to save the file as on the browser.

The virtual path is set via VirtualPath property (surprise surprise!). Note that I could have chosen to make this parameter accept the full file path instead of a virtual path, but I didn’t want to force users of this class to fake out a Server.MapPath call in a unit test. In any case, the change is trivial for those who prefer that approach. I might add overloads that accept a Stream, etc...

The file download name is set via the FileDownloadName property. Notice that this is the filename that the user is prompted with.

If the FileDownloadName property is set, the ExecuteResult method makes sure to add the correct content-disposition header which causes the browser to prompt the user to save the file.

For those familiar with Design Patterns, action results follow the pattern commonly known as the Command Pattern. An action method returns an instance that embodies an command that the framework needs to perform next. This provides a means for delaying the execution of framework/pipeline code until after your action method is complete, rather than from within your action method, which makes unit testing much nicer.

Speaking of unit tests, here’s the unit test for that download action method I wrote. As you can see, it is quite simple.

[TestMethod]
public void DownloadActionSendsCorrectFile() {
  var controller = new HomeController();

  var result = controller.Download() as DownloadResult;

  Assert.AreEqual("TheSiteCss.css", result.FileDownloadName);
  Assert.AreEqual("~/content/site.css", result.VirtualPath);
}

Here’s the code for the DownloadResult class. This is the class that does all the work (not that there is much work to do). I do have unit tests of this class in the included source code which demonstrate how to unit test a custom action result.

public class DownloadResult : ActionResult {

  public DownloadResult() {
  }

  public DownloadResult(string virtualPath) {
    this.VirtualPath = virtualPath;
  }

  public string VirtualPath {
    get;
    set;
  }

  public string FileDownloadName {
    get;
    set;
  }

  public override void ExecuteResult(ControllerContext context) {
    if (!String.IsNullOrEmpty(FileDownloadName)) {
      context.HttpContext.Response.AddHeader("content-disposition", 
        "attachment; filename=" + this.FileDownloadName)
    }

    string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);
    context.HttpContext.Response.TransmitFile(filePath);
    }
}

To see all the code, download the source. It includes a simple demo web app and all unit tests.

Technorati Tags: ,

DotNetRocks Part Deux

I did another interview with those wild and crazy guys, Carl and Richard. My first time (show 261) being on .NET Rocks was back in August of 2007 when I talked about Subtext, Open Source, and my blog.

This time (show 339), the interview focused on my experiences with working at Microsoft and the work I do on the ASP.NET MVC project. Notice that my profile pic hasn’t changed at all. I should do something about that.

I haven’t listened to it yet as I hate hearing myself talk. Even worse is seeing myself on video.

In any case, if you are interested, take a listen to the latest .NET Rocks show.

Oh, and I was also a guest on Hanselminutes recently. That talk was recorded at ALT.NET Open Spaces in Seattle. It’s only a coincidence that these two interviews came out a week apart. They were recorded more than a week apart. Honestly, I’m not a microphone whore. I’m more of a camera whore. ;)

Technorati Tags:

RouteEvaluator For Unit Testing Routes

A while back I wrote a routing debugger which is useful for testing your routes and seeing which routes would match a given URL. Rob suggested we have something like this for unit tests, so I whipped something simple up.

This is a class that allows you to test multiple different URLs quickly. You simply create the RouteEvaluator giving it a collection of routes and then GetMatches which returns a List<RouteData> containing a RouteData instance for every route that matches, not just the first one.

Here's a sample of usage.

[Test]
public void CanMatchUsingRouteEvaluator()
{
  var routes = new RouteCollection();
  GlobalApplication.RegisterRoutes(routes);

  var evaluator = new RouteEvaluator(routes);
  var matchingRouteData = evaluator.GetMatches("~/foo/bar");
  Assert.IsTrue(matchingRouteData.Count > 0);
  matchingRouteData = evaluator.GetMatches("~/foo/bar/baz/billy");
  Assert.AreEqual(0, matchingRouteData.Count);
}

And here’s the code. Note that my implementation relies on Moq, but you could easily implement it without using Moq if you wanted to.

public class RouteEvaluator
{
  RouteCollection routes;
    
  public RouteEvaluator(RouteCollection routes)
  {
    this.routes = routes;
  }

  public IList<RouteData> GetMatches(string virtualPath)
  {
    return GetMatches(virtualPath, "GET");
  }

  public IList<RouteData> GetMatches(string virtualPath, string httpMethod)
  {
    List<RouteData> matchingRouteData = new List<RouteData>();

    foreach (var route in this.routes)
    {
      var context = new Mock<HttpContextBase>();
      var request = new Mock<HttpRequestBase>();

      context.Expect(ctx => ctx.Request).Returns(request.Object);
      request.Expect(req => req.PathInfo).Returns(string.Empty);
      request.Expect(req => 
req.AppRelativeCurrentExecutionFilePath).Returns(virtualPath);
      if (!string.IsNullOrEmpty(httpMethod))
      {
        request.Expect(req => req.HttpMethod).Returns(httpMethod);
      }

      RouteData routeData = this.routes.GetRouteData(context.Object);
      if (routeData != null) {
        matchingRouteData.Add(routeData);
      }
    }
    return matchingRouteData;
  }
}

Let me know if this ends up being useful to you.

Technorati Tags: ,,

Code Based Repeater for ASP.NET MVC

Not long ago, my compadre Scott Hanselman related the following story...

In a recent MVC design meeting someone said something like "we’ll need a Repeater control" and a powerful and very technical boss-type said:

"We’ve got a repeater control, it’s called a foreach loop."

I beg to differ. I think we can do better than a foreach loop. A foreach loop doesn’t help you handle alternating items, for example. My response to this story is, “The foreach loop is not our repeater control. Our repeater control is an iterating extension method with lambdas!”. Because who doesn’t love lambdas?

Not many people realize that within an ASPX template, it’s possible to pass sections of the template into a lambda. Here, let me show you the end result of using my Repeater<T> helper method. It’s an extension method of the HtmlHelper class in ASP.NET MVC.

<table>
  <% Html.Repeater<Hobby>("Hobbies", hobby => { %>
  <tr class="row">
    <td><%= hobby.Title %></td>
  </tr>
  <% }, hobbyAlt => { %>
  <tr class="alt-row">
    <td><%= hobbyAlt.Title %></td>
    </tr>
  <% }); %>
</table>

This renders a table with alternating rows. The Repeater method takes in two lambdas, one which represents the item template, and another that represents the alternating item template.

This particular overload of the Repeater method takes in a key to the ViewData dictionary and casts that to an IEnumerable<T>. In this case, it tries to cast ViewData["Hobbies"] to IEnumerable<Hobby>. I’ve included overloads that allow you to explicitly specify the items to repeat over.

This isn't very remarkable when you think a bout it. What the above template code translates to is the following (roughly speaking)...

Response.Write("<table>");

Html.Repeater<Hobby>("Hobbies", hobby => {
    Response.Write("  <tr class=\"row\">");
    Response.Write("    <td>");
    Response.Write(hobby.Title);
    Response.Write("    </td>");
    Response.Write("  </tr>");
  }, hobbyAlt => { 
    Response.Write("  <tr class=\"alt-row\">");
    Response.Write("    <td>");
    Response.Write(hobbyAlt.Title);
    Response.Write("    </td>");
    Response.Write("  </tr>");
  });

Response.Write("</table>");

The code for the Repeater method is simple, short, and sweet.

public static void Repeater<T>(this HtmlHelper html
  , IEnumerable<T> items
  , Action<T> render
  , Action<T> renderAlt)
{
  if (items == null)
    return;

  int i = 0;
  items.ForEach(item => {
    if(i++ % 2 == 0 ) 
      render(item);
    else
      renderAlt(item); 
  });
}

public static void Repeater<T>(this HtmlHelper html
  , Action<T> render
  , Action<T> renderAlt)
{
  var items = html.ViewContext.ViewData as IEnumerable<T>;
  html.Repeater(items, render, renderAlt);
}

public static void Repeater<T>(this HtmlHelper html
  , string viewDataKey
  , Action<T> render
  , Action<T> renderAlt)
{
  var items = html.ViewContext.ViewData as IEnumerable<T>;
  var viewData = html.ViewContext.ViewData as IDictionary<string,object>;
  if (viewData != null)
  {
    items = viewData[viewDataKey] as IEnumerable<T>;
  }
  else
  {
    items = new ViewData(viewData)[viewDataKey] as IEnumerable<T>;
  }
  html.Repeater(items, render, renderAlt);
}

Some of the ViewData machinations you see here is due to the fact that ViewData might be a dictionary, or it might be an unknown type, in which case we perform the equivalent of a DataBinder.Eval call on it using the supplied view data key.

It turns out that the regular <asp:Repeater /> control works just fine with ASP.NET MVC, so there’s no need for such an ugly method call. I just thought it was fun to try out and provides an alternative approach that doesn’t require databinding.

UPDATE: I wanted to end this post here, but my compadre and others took exception to my implementation. Read on to see my improvement...

As astute readers of my blog noted, the example I used forces me to repeat a lot of template code in the alternative item case. The point of this post was on how to mimic the repeater, not in building a better one. Maybe you want to have a completely different layout in the alternate item case. I was going to build a another one that relied only on one template, but I figured I would leave that to the reader. But noooo, you had to complain. ;)

So the following is an example of a repeater method that follows the most common pattern in an alternating repeater. In this common case, you generally want to simply change the CSS class and nothing else. So with these overloads, you specify two CSS classes - one for items and one for alternating items. Here’s an example of usage.

<table>
  <% Html.Repeater<Hobby>("Hobbies", "row", "row-alt", (hobby, css) => { %>
  <tr class="<%= css %>">
    <td><%= hobby.Title%></td>
  </tr>
  <% }); %>
</table>

And here’s the source for the extra overloads. Note that I refactored the code for getting the enumerable from the ViewData into its own method.

public static void Repeater<T>(this HtmlHelper html
  , IEnumerable<T> items
  , string className
  , string classNameAlt
  , Action<T, string> render)
{
  if (items == null)
    return;

  int i = 0;
  items.ForEach(item =>
  {
    render(item, (i++ % 2 == 0) ? className: classNameAlt
  });
}

public static void Repeater<T>(this HtmlHelper html
  , string viewDataKey
  , string cssClass
  , string altCssClass
  , Action<T, string> render)
{
  var items = GetViewDataAsEnumerable<T>(html, viewDataKey);

  int i = 0;
  items.ForEach(item =>
  {
    render(item, (i++ % 2 == 0) ? cssClass : altCssClass);
  });
}

static IEnumerable<T> GetViewDataAsEnumerable<T>(HtmlHelper html, string viewDataKey)
{
  var items = html.ViewContext.ViewData as IEnumerable<T>;
  var viewData = html.ViewContext.ViewData as IDictionary<string, object>;
  if (viewData != null)
  {
    items = viewData[viewDataKey] as IEnumerable<T>;
  }
  else
  {
    items = new ViewData(viewData)[viewDataKey] 
      as IEnumerable<T>;
  }
  return items;
}

Hopefully that gets some people off my back now. ;)

Technorati Tags: ,