January 2008 - Posts

Frequently in web applications there's a requirement like this: "Every page should display [foo]." Where "foo" can be literally anything: a list of favorites, sponsors, news, or whatever - it's data. In what I'll call "normal, by a long shot" ASP.NET cases, universally displayed data will probably be encapsulated in a user control and put on a master page. The master page includes the user control on all pages, and the user control loads the data and renders it, and it was all declared Good.

The MVC world (ASP.NET "unplugged") turns everything on it's head. Maintaining a clean separation of concerns means the controller is responsible for ensuring the appropriate data is made available to the appropriate view. The view does not load data on it's own, nor does it know or care where the data came from. And the view was declared Dumb.

Views aren't really dumb, of course, they have to render data, and the MVC framework does give them some tools to help. For example, the ViewMasterPage base class exposes helpers for constructing links, accessing the view data, etc.

So what do you do in the ASP.NET MVC world when you've got a "user control" embedded in your master page that needs specific data? Let's say that hypothetically speaking you're working on a CodeCampServer project, and you need to, oh I don't know, display a list of code camp Sponsors (ahem, "Contributors") on every single page?

The way I see it, you've got three options:

1. "Global" data for global display

Data can be loaded at a low level for each and every request. Something like:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    this.Context.Items["FooData"] = new FooData();
}

The pro is that FooData will be available to every view (though not through the standard ViewData). Unfortunately if this data is expensive to compute (or even if it isn't) it'll be loaded for every request, no matter what. Oh, and no one in their right mind would actually do it this way. :) There's that, too.

2. Controller.OnPreAction

The main Controller class exposes a virtual OnPreAction method which offers a hook just before an action is invoked. For global data, all controllers in your application could derive from a common base controller class which loads the necessary data.

protected override bool OnPreAction(string actionName, System.Reflection.MethodInfo methodInfo)
{
    ViewData["FooData"] = new FooData();
    return base.OnPreAction(actionName, methodInfo);
}

Views will be able to access the data via the ViewData property, as normal. With single inheritance in C#, a required base class just for common data can potentially cause problems.

3. Call "RenderAction" from the view

RenderAction is an extension method that allows a view to reference a Controller and Action and have the results be rendered in-place within the view.

The "Tags" controller and "Cloud" action would render a view that generated an HTML fragment that would fit within the current view. This approach has advantages of only loading data when absolutely necessary and changing your view doesn't necessarily require changing the application code to match.

There could be questions about separation of concerns: a view is determining what data to include regardless of what it's controller gave it. But the same is true for the other solutions as well - this is just more dynamic.

Another advantage to this approach is that if you set it up right, with no extra work your "viewlets" (the views that render fragments) can return JSON serialized data for an AJAX call, for example.

I'm pro-RenderAction, but in discussions I've had with people it hasn't been as big a hit with them. What are your thoughts? Am I missing some pros or cons? Are there more options that I didn't include? Is it really just an app-by-app decision, or is there an emerging convention that should be followed?

It's amazing how the process of making an application localizable can be both simple and confusing at the same time. At a basic level, the Visual Studio designer makes it very easy. Set the "Localizable" property on your form to True and you're good to go. It's very convenient.

On the other hand, there are a lot of challenges like poorly named properties, confusing defaults, and possibly unexpected behavior. You really need to understand what's going on under the covers.

For instance, if you've looked into .NET localizability at all, you probably know that by default resources are loaded using the CultureInfo retrieved from the System.Threading.Thread.CurrentThread.CurrentUICulture property. That's why most guidance for manually changing the default application UI language says to set that property to a value of your choice. And well you should - it's the only way to override the default language settings for your Windows Forms.

See, the Windows Forms designer automatically generates all sorts of useful code that you can't (and shouldn't) touch. Here's an example.

image

Notice the ComponentResourceManager? It's a local variable in the InitializeComponent method. The only way to alter it's behavior is to alter the current thread's UI culture information.

So that's great, and even better it's easy. When your application starts, you can load culture/language information from whatever persistence mechanism you prefer, and essentially "set it and forget it", since all Controls should be created on the same thread.

image

But there's one problem with this: it only applies to UI elements (Controls).

It's also common to load objects outside the context of the UI, perhaps with the intention of later presenting them to the user, or persisting to a file, or something else "user visible". For this, you can add an arbitrary number of resx files that contain images, strings, serialized objects, etc.

image

With Visual Studio 2005 and higher, these resources are available as strongly-typed properties, giving you compile-time checking and ease of use.

image

But here's where our problem comes in. Overriding the default UI culture on the main thread applies only to that thread. If you load any resources from a background thread of any kind, they will be the resources for the default windows UI language.

The strongly-typed class ("Resources", in my example) offers us some relief, however, in the form of a static "Culture" property on the auto generated class. If you never touch the property, resources will be loaded using the current thread value. But if you assign your own value, you can explicitly control which resources are loaded.

Consider this debugging example where I'm stopped in the execution of a background thread. I've created a global static "Localization" class with a "CurrentCulture" property that I use to maintain the current UI language for my application. You can compare and contrast the values set for the current thread and the auto generated Resource static class, which my application has initialized. (I'm running the English UI for Windows, by the way.)

image

So, why am I posting this? Because I myself was unsure about how to correctly accomplish the goal of enabling users to select their own UI language until I noticed the auto generated "Culture" property on the Resources class. And it took me embarrassingly long to find it. Hopefully this post keeps somebody else from wasting an evening, or more.

In summary, it's actually simple once you understand all the details. To enable a specific "non-default" language in your application:

  • Set the System.Threading.Thread.CurrentThread.CurrentUICulture property to the appropriate culture, and
  • Initialize all auto generated resource classes (or any custom uses of the ResourceManager class) to the appropriate culture, before loading any resources.

imageVisual Studio 2005 and 2008 have built-in support for refactoring code, including renaming namespaces, classes, variables, and more. Add-ins like Resharper also have support for refactoring by renaming. These tools work great, and have good integration into the Visual Studio IDE, for example being able to preview each change and exclude false positive matches. I know many people swear by Resharper in particular (for more features than the refactoring alone).

Unfortunately all of these tools are restricted in one normally insignificant way: they only operate on the currently loaded project(s). For most software this really is insignificant. The entire application can be loaded into the IDE all at once. Sometimes, though, it is a problem--usually when there's another problem at play: code "bloat" (via Jeff).

If you've ever changed a namespace and wanted to simply push the change into every code file in an entire directory branch, PowerShell comes to our rescue with it's search-and-replace capabilities. I wrote the following "Move-Namespace" function to do just that.

Invoke the function like so: "Move-Namespace *.cs "[current namespace]" "[current class name]" "[new namespace]".

After it completes, any explicit reference will be changed, and any implicit references (using traditional "using [namespace];" statements) will have a new "using [new namespace];" line inserted after the last existing using statement.

Let's hope you don't have a code base that requires this function. :) But if you do, I hope it helps!

Note that this function relies on my "Replace-String" function, found here.

function Move-Namespace(
    $includes = $(throw 'Specify a file filter to use'), 
    $oldNamespace = $(throw 'The namespace to replace - i.e. MyCompany.Product'), 
    $className = $(throw 'The class name to search for - i.e. MyClass'), 
    $newNamespace = $(throw 'The new namespace to add - i.e. MyCompany.Product.Feature'))
{    
    # Look for cases of $className
    # or $oldNamespace.$className
    # and replace as necessary
    
    # First check for assumed "using" statements, and add the using statement
    $results = @{}
    $files = get-childitem -r -i $includes | select-string "(?<!$oldNamespace\.)\b$className\b" -list |% { $_.Path }
    select-string "^using.*;" -path $files | group-object Path | select-object Name, @{Expression={ ($_.Group | measure-object -property LineNumber -max).Maximum }; Name="LastUsingStatement" } |% { $results[$_.Name] = $_.LastUsingStatement }
    foreach ($key in $results.keys)
    {
      $processFile = $true
      $lastUsingIndex = $results[$key]
      $fileContents = get-content $key
      # look for existing using statement and cancel processing this file
      for ($i = 0; $i -lt $lastUsingIndex; $i++)
      {
        if ($fileContents[$i] -match "using $newNamespace;")
        {
          # this file already has the correct using statement, stop processing
          Write-Warning "File $key already has the required using statement, ignoring"
          $processFile = $false
          break;
        }
      }
      
      if ($processFile)
      {
        $newContents = $fileContents[0..($lastUsingIndex-1)] + "using $newNamespace;" + $fileContents[$lastUsingIndex..$fileContents.Length]
        set-content -path $key -value $newContents
        Write-Host "Successfully updated file $key" -foregroundcolor Green
      }
    }
    
    # Next check for explicit references
    replace-string "\b$oldNamespace\.$className\b" "$newNamespace.$className" $includes
}

(For those who'd rather just download the code, the link is at the end.)

Lately I've been delving into the ASP.NET MVC framework, the timing of which has been interesting as I am about to finish reading RESTful Web Services by Leonard Richardson and Sam Ruby. It's an excellent book that I'd highly recommend to anybody designing and creating services for the web, or just interested in it.

This quote from the book description on Amazon.com succinctly describes what's been floating around in my head for a while:

You've built web sites that can be used by humans. But can you also build web sites that are usable by machines? That's where the future lies, and that's what RESTful Web Services shows you how to do.
[Emphasis mine]

Why do we have to build web "sites" for humans and different web services for machines? I can create an ASP.NET site, a SOAP-based ASMX web service, or a SOAP or REST (with .NET 3.5) WCF service and while they can share business logic, there's still a lot of "endpoint code" (for lack of a better term) that has to be implemented, and if I want a human friendly front end + a web API, I have to build a site that essentially wraps another service, since the site itself really is a service.

With the ASP.NET MVC Framework, though, I see the opportunity to unify services and "sites" (human-readable services). Imagine visiting http://www.example.com/books/ and being able to receive back HTML, POX, or JSON, depending on what you asked for--with no additional code required outside of following a convention or two. Web browsers ask for HTML, returning a human consumable page that gets rendered. AJAX or custom clients can request JSON or XML, whatever they know how to interpret best.

The separation of concerns and extensibility that the ASP.NET MVC framework exposes lets us accomplish this with surprisingly little code. At least for read-only services (which is as far as I took it for now.) If you're not familiar with how the ASP.NET MVC framework works, check out Eilon's excellent ASP.NET MVC design philosophy post (he also includes many good reference links).

Approach

Outside of the ASP.NET MVC framework itself, from an HTTP perspective I simply want to alter my response format depending on what the incoming request asked for. I decided to keep it simple and only support the "text/html", "application/xml", and "application/json" media types.

I started off trying to use the "Accept" HTTP header as an indicator for what response format the client preferred. After all, that's what the Accept header is for: "The Accept request-header field can be used to specify certain media types which are acceptable for the response." But I quickly ran into a snafu when I found that Firefox prefers XML over HTML.

image

That means that every request Firefox makes would result in the uber-friendly XML view.

image

And because of that snafu, I read up some more on the Accept header - namely what they had to say about it in an appendix of RESTful Web Services:

Hiding this information inside the HTTP headers is a good idea for web browsers, but it shouldn't be the only solution for web service clients. I recommend exposing different representations using different URIs. ... If you want to support Accept on top of this, that's great (Rails does this too).

There was also some pragmatic advice in the chapter on Representations.

It's RESTful to keep this information [file format types] in the HTTP headers, and it's RESTful to put it in the URI. I recommend keeping as much of this information as possible in the URI, and as little as possible in request metadata. I think URIs are more useful than metadata. URIs get passed around from person to person, and from program to program. The request metadata almost always gets lost in translation.

I tend to agree with that. To make it easy and somewhat intuitive, I chose to base the response format on the existence of a querystring parameter named "format". It can specify "xml" or "json", for example http://www.example.com/books/?format=json. But really, you can use any method you want to allow format specification.

Implementation

Formatting the response is the responsibility of the View in the MVC architecture. Since processing requests for HTML content didn't need to change from the default ASP.NET MVC behavior, all I need to do is override the default WebFormViewFactory functionality and return my own "SerializedView" if the URI indicated xml or json formatting. SerializedView is an abstract base class that encapsulates common functionality and allows sub-classes to perform the serialization, or handle custom error reporting.

image

When a Controller passes control to a View, it calls the "RenderView" method on the Controller base class, passing in the view name and some optional data - including something called "ViewData" - an object that represents the content the view should render.

Here's the best part: creating an ASP.NET MVC site+service using my approach means passing a serializable object as ViewData. It's as simple as that, nothing else needs to change (ok, you need one additional line of code in your Application_Start handler - more on that later). In your ASPX view, you can access the object via ViewData and use all the HTML helpers, UserControls, etc. that you want to build out your HTML, javascript, and who-knows-what.

Unfortunately, the current CTP of the ASP.NET MVC framework does not expose a way to "inject" a different IViewFactory (my subclassed version) into the default Controller created by the ControllerBuilder class. So, that means implementing the IControllerFactory interface and duplicating (ouch!) the code that ControllerBuilder executes, with one somewhat nasty addition.

image

Yuck! The ViewFactory property is specific to the Controller class, not the IController interface, hence the specific processing -- and that also means my approach won't work for other IController implementations. Not without some extra work, anyway.

This is where the only change you need to make comes in. My IControllerFactory implementation needs to be registered as the default controller factory, which is done in the Application_Start handler - the same place your routes are configured.

image

Serializable Types

For various reasons, you should use the WCF [DataContract] and [DataMember] attributes to tag your objects as serializable. For one, you gain all the serialization benefits these attributes offer, plus you can use the same contract types across multiple endpoints if you need to. And for another, serializing to JSON via the System.Runtime.Serialization.Json.DataContractJsonSerializer requires the DataContract attribute. If you use the older [Serializable] attribute, you get some interesting results.

image

The deprecated System.Web.Script.Serialization.JavaScriptSerializer works correctly for objects tagged with [Serializable] but it's, well, deprecated.

Example

Using the default ASP.NET MVC Application templates, I registered the default ControllerFactory in my Application_Start handler, defined a Books controller with a default action, and a "List" view that prints out some Author and Title information. I also created the following definition of a book.

image

My controller implementation is no different than it normally would be. I get a list of books and render the "List" view, explicitly specifying the book list as the ViewData. You might notice that I pass the ControllerContext into the GetBooks method. I am experimenting with returning URIs to related resources as part of the data. I'm still playing around, but for now you get my latest attempt. :)

image

Requesting the appropriate URI in Firefox yields the expected result from my List view:

image

And altering the URI returns the appropriate response types, xml or json.

image

image

Summary

This ended up being a longer post than I intended, but I hope it was worth it. I've put the full source for the "MvcServiceLibrary" and an example up on my Tools page - but here's the direct download link. I'll also be checking if this is worth putting into the MvcContrib open source project.

I think that the ASP.NET MVC framework is a huge step forward in creating RESTful web sites, and hopefully now also RESTful web services! Let me know what you think, I love getting feedback.

More Posts