Saturday, May 03, 2008

ASP.NET MVC Preview 2 Update

I’m upgrading Code Camp Server to the latest drop of ASP.NET MVC (which is just an interim release, so you might not yet care) but I wanted to get some first hand knowledge of how the new changes work out.

I won’t go over every change, because The Gu has a great writeup already.  But there are a few thoughts I’d like to share.

Routing

Routing is getting mature, and quick.  The fact that we can use this in our WebForms projects is really cool.  With routing getting more and more features, it is critical that you have extensive tests around your routes.  Imagine that you have an application already deployed.  A single change to the routing assembly (or the registered routes) can have a drastic effect on your application.  Existing links could be broken, new links might not work the way you expected, and you will severly cripple your search engine ranking.

Code Camp Server definitely needs to get an exhaustive set of routing tests to verify each of our URLs is functioning properly.

ActionResult

Instead of having controllers directly render views or redirect to another action, they have extracted this into ActionResult classes.  While the syntax is a bit quirky (“return RenderView”) this is a good design choice in my opinion.  If C# were as flexible as Ruby, we could still have the single “RenderView” method call, and the last statement would actually get returned to the calling method.

Our tests for Code Camp Server will be a lot cleaner.  No more test subclasses.  A lot less mocking as well, since now you can rely on plain old Asserts on the ActionResult .

This ActionResult change never made it to ComponentController though, but it will likely be there in the next drop.

Aside from the readability hiccup with “return” I don’t see any downsides to this change.  Anybody else experience any?

Validation Love from Steven Sanderson

Some people wonder how to do validation, since the existing ASP.NET Validators don’t work with ASP.NET MVC.  I ran across this, where Steven Sanderson shows how to do Model-based Client-side Validation in ASP.NET MVC using Castle Validator.  There’s even a 6–minute screencast!  Keep that kind of stuff coming Steve! 

Friday, May 02, 2008

I'm an Insider

Quick update:  I've been invited to join ASP Insiders!  I'm proud to be part of a select group of ASP.NET professionals who reach out to the community via forums, blogs, conferences, etc and are generally recognized experts in ASP.NET.



Some of the benefits of being an ASP Insider is to get in on some private discussion about design, new features, etc.  While I cannot publish any of this on my blog, I will be able to provide feedback in some areas (such as ASP.NET MVC).

Now, to go learn the secret handshake..
Saturday, March 15, 2008

Debugging Routes

Previously Scott Hanselman pointed out that in CodeCampServer we are using a tiny wrapper around MvcRouteHandler in order to catch all exceptions and provide the route information along with the error message.  I called this (for lack of a better name) BetterMvcRouteHandler.  That’s right, Better!

Scott took this a mile further and created a nice route debugging table that you can use while debugging.  You can download this route debugger on Phil’s blog (with source).

To enable the route table debugger, you have to add this line to your global.asax:

RouteDebug.RouteDebugger.DebugRoutes(RouteTable.Routes);

I think it would be better to be able to add ?routedebug to the end of ANY url and have this happen automatically.  To accomplish this I created an HttpModule.  It looks at each request for the ?routedebug querystring key and, if found, calls the line above.  I ran into a hiccup, though… once you set the route handler to the debug one, all future requests use it as well (even without the url parameter). 

Good ‘ol Phil released the source for this route debugger, so I added a method to revert the routes back to their original values if the query parameter doesn’t exist.

Here is the RouteDebugModule:

Route_debug_module

(remember:  this code would NOT be appropriate for production use, both for the awkward setting of route handlers on every request and the fact that you don’t want your users to be able to see your route information.)

And the RevertRouteHandlers method (in Phil’s RouteDebugger.cs):

Revert_routes

The _oldRouteHandlers is just a static dictionary that hangs on to the route before swapping with the debug one.

I added the HttpModule to my web.config, and now I can do this:

Route-debug-in-action

…and also flip back to the regular request without recompiling.

I’m thinking they should call this BettererRouteHandler instead of RouteDebugHandler, who’s with me?

Monday, March 10, 2008

ASP.NET MVC - Preview 2 Thoughts

I said previously that I was excited for this release.  Ultimately I didn’t see much worth blogging about.  Some features are a step in the right direction, some (I think) were a step back.

I’m currently upgrade Code Camp Server to Preview 2 and hit about 80 errors which I had to go in and resolve.  My initial thoughts:

  • I think the HttpContextBase is a bad idea.  Yes I understand the desire to not introduce breaking changes.  But I think that the people that will be using ASP.NET MVC will be congnizant of this and code appropriately.  Interfaces are much easier to depend work with while testing, and I’d rather just use those.  Hey, couldn’t they give us both, and framework providers could choose?
  • what’s up with the OnActionExecuting, OnActionExecuted events?  These used to be OnPreAction, OnPostAction, which was much more readable (in my opinion) — seriously, put these back!  The new naming is not nearly as obvious to the glance.
  • Testing is still too cumbersome.  I think some good mocking-fu can mitigate this, but it’s a tough sell to people new to unit testing (and to mocking).  TempData should be implemented as an IDictionary on the controller, only to have a real TempData that depends on session state at runtime.  This would allow me to test that TempData works without having to mock session (which is not intuitive at all).
  • The Action filters are going to be where the goo lives.  This is how we will see some really productive & handy filters for performing actions such as requiring SSL, protecting an action from unauthenticated access, loading common data, etc.  Think of them as HttpModules for individual actions.

More to come, I’m sure…

 

 

Wednesday, March 05, 2008

ASP.NET MVC Preview 2 Now Available

I normally don't do this sort of announcement, but this one excites me.

Download the ASP.NET MVC Preview 2 here:
http://www.microsoft.com/downloads/details.aspx?familyid=38cc4cf1-773a-47e1-8125-ba3369bf54a3&displaylang=en&tm

Feedback to come...

Wednesday, February 27, 2008

Shame On Me

Someone decided to publicly criticize my example code in my previous post by saying that it was “bad.”  While I don’t agree with the nature of the criticism (my blog *does* have comments, you know) I totally agree with the message.  It was bad code.  I would never place that in a production application.

The intent of the post was to demonstrate some ajax techniques, not to talk about how to write testable, decoupled controllers.  I suppose that example code should come with a bit of a disclaimer that says “hey, we are doing this here for simplicity’s sake, but obviously you’d abstract this out.”  Ideally I would have commented out the entire flickr section, because it’s totally not important in the context of the example.

In light of this topic, here is how a perfectly testable PhotosController might be written:

public class PhotosController : Controller
{        
    private readonly IPhotoRepository _photoRepository;

    public PhotosController(IPhotoRepository photoRepository)
    {
        _photoRepository = photoRepository;
    }

    [ControllerAction]
    public void Search()
    {            
        RenderView("Search", new Photo[] { });
    }        

    [ControllerAction]
    public void Find(string query, bool? ajax)
    { 
        
        var photos = _photoRepository.SearchPhotos(query);
          
        ViewData["query"] = query;
        ViewData["photos"] = photos;
        
        if(ajax == true)
        {
            RenderView("_images", photos);
        }
        else
        {
            RenderView("results");    
        }            
    }
}

Here you can clearly see our dependency on IPhotoRepository.  This guy can be anything we want, from a Flickr implementation, google images implementation, a file system directory, or even (gasp) a TEST implementation for writing unit tests!

Here is the IPhotoRepository, that we are using in the example:

public interface IPhotoRepository
{
    Photo[] SearchPhotos(string query);

asdf

}

This returns a Photo object (that I defined), which means whatever implementation you choose, you need to map to this object. This is a good thing because it removed my dependency on "FlickrPhoto" in the view itself.

Our FlickrPhotoRepository now looks like this:

public class FlickrPhotoRepository : IPhotoRepository
{
    private readonly IConfigSource _ConfigSource;

    public FlickrPhotoRepository(IConfigSource configSource)
    {
        _ConfigSource = configSource;
    }

    public Photo[] SearchPhotos(string query)
    {
        var flickr = new Flickr(_ConfigSource.GetSetting("flickr.api.key"));
        var results = flickr.PhotosSearch(query, TagMode.AnyTag, query, 40, 1);

        var photos = new List<Photo>();
        foreach (var item in results.PhotoCollection)
        {
            photos.Add(new Photo(item.SquareThumbnailUrl, item.Title, item.PhotoId));
        }

        return photos.ToArray();
    }
}

Wait a minute! This class now has it's own dependency on IConfigSource? Craziness! In our example code from before, I had accessed the Configuration singleton directly, which is punishable by hand-removal in Albonia. Thank goodness I don't live there. Now this class is decoupled from the actual configuration implementation and it could really be coming from a text file or database (whatever you want).

Wiring all this up is easy.  You could do it via XML, but I chose to do it directly in code:

private void InitializeWindsor()
{
    _container = new WindsorContainer();
    _container.AddComponent("config-source", typeof(IConfigSource), typeof(AppDomainConfigSource));        
    foreach(Type type in Assembly.GetExecutingAssembly().GetTypes())
    {             
        if(type.IsSubclassOf(typeof(Controller)))
        {
            _container.AddComponent(type.Name, type);
        }
    }
}

Also note that I'm using WindsorControllerFactory from MVCContrib, so now my controller can be created (and each of it's dependencies ( and their dependencies (and their Dependencies)))... I think you get the idea.

And there you have it.  A perfectly decoupled and testable modification for my previous example code.  As you were….

Sunday, February 24, 2008

UpdatePanel, Who?

I don’t know why people are so enamoured with ASP.NET AJAX.  Sure it works and is developed by Microsoft, but what’s wrong with the dozen other alternatives out there?  Many developers I talk to will say “yeah I used Ajax on this” (with a capital A) and what they are referring to is ASP.NET AJAX.  While I don’t think it is a horrible framework (I use it on occasion), I think it is too complicated and requires too much to get a simple example started.  When people say they have ASP.NET AJAX experience, it means to me that they have experience with declarative controls and <ScriptManager> tags.  It doesn’t necessarily mean they know how ajax works and it definitely doesn’t mean they know anything about javascript.  This worries me.

I often talk about how awesome prototype is (and lately I’ve been digging jquery) and inevitably someone tells me it’s just eaiser to wrap your sections in an UpdatePanel and be done with it.  It is sometimes hard to argue with that, even though it is the most inefficient way of doing ajax.  In addition, this can cause side-effects in WebForms because the entire lifecycle is being executed, even though only part of the page is rendered.  This method is actually called “partial html replacement” or “partial page rendering” and can be achieved easily without needing ASP.NET AJAX.

Now that ASP.NET MVC is out, which doesn’t work with ASP.NET AJAX (not the full stack) people are foaming at the mouth for their UpdatePanel equivalent.  (I think we’ll see an update for this in the coming weeks that will show ASP.NET AJAX working specifically in the MVC environment, but this point still stands.  You don’t have to use it.)

I’m going to use jquery in my sample, but the technique will apply to prototype, mochikit, and others.  In this sample we will take a functioning page and turn it into an ajax enabled page, while preserving the non-ajax scenario (if javascript is disabled).

For our example, I will borrow from an impressive rails tutorial [Quicktime] and implement the same thing in ASP.NET MVC.  The basic concept is this:  Search for flickr photos and display them on the page using ajax. 

We have our page:

Photo-search-form

if you search for “flowers”, you are taken to another action (physically) that shows you the results:

Photo-search-results

Now let’s assume that you want this to happen with ajax.  We’d like the results to be displayed directly on the same page as the query.

Here’s the form from that first action:

<h1>Photo Search</h1>
    <div class="box">
        <form id="search-form" method="get" action="/photos/find">   
            <label for="query">Query:</label>
            <input type="text" id="query" name="query" />
            <input type="submit" id="search" value="search" />
            <img id="indicator" style="display:none" src="../../content/load.gif" alt="loading...." />
        </form>
    </div>

And the action that the form submits to:

[ControllerAction]
public void Find(string query, string format)
{ 
     var flickr = new Flickr(ConfigurationManager.AppSettings["flickr.api.key"]);
     var photos = flickr.PhotosSearch(query, TagMode.AnyTag, query, 40, 1);

     ViewData["query"] = query;
     ViewData["photos"] = photos;

     RenderView("results");
}

(This uses the FlickrNet library available on CodePlex)

In order to translate this into an ajax-enabled page (while preserving no-javascript behavior) we have to do 2 things. 

  • Prevent the form from posting, so that the browser doesn’t take us off the page
  • Make the Find() action render only the partial html for the images (in the case of an ajax call

Let’s solve the first problem first.  Using jquery, we can capture the form submit by doing this:

function init()
{        
    $('#search-form').submit(        
        function() {
            $('#photos').hide();
            $('#indicator').show();  
            hijack(this, loadPhotos, "html"), 
            return false;
        });
}

$(document).ready(init);

When the document has been loaded, init() is called.  This finds our form and adds an onsubmit handler to it.  Next, we hide the photos div (in case there were pictures there already) and then we start the ajax indicator.  For the love of all that is good and holy, let the user know that you’re doing something!  Ok next up is this strange hijack() function.  This guy takes the form and submits it via ajax instead.  It accepts the form, the method to call when the result comes back (callback), and the data type of the return value.  Finally, to cancel the browser from actually submitting the form, we return false.

Here is that “photos” div I mentioned a second ago:  (I put this directly under our form)

<div id="photos" class="box" style="display: none">
</div>

It’s initially hidden because it will be empty when the page physically loads.

hijack() is a generic javascript function, and is defined as:

function hijack(form, callback, params, format)
{   
    try {                                            
        $.ajax({
            url:        form.action,
            type:       form.method,
            data:       $(form).serialize() + "&ajax=true",
            dataType:   format,
            success:    callback,
            failure:    function(e) { alert("error! " + e); }
        });
    } catch(e) { 
        alert(e);
    }
}

This is pretty basic ajax stuff in jquery.  The only thing different that I am doing here is notifying the controller that this is an ajax call, so that we may make it behave a little differently.  After all, we don’t want it to return a full html document… we only want the images!

That’s the essense of issue #2.  If we revisit our controller action, we can make the following change:

[ControllerAction]
public void Find(string query, bool? ajax)
{ 
    var flickr = new Flickr(ConfigurationManager.AppSettings["flickr.api.key"]);
    var photos = flickr.PhotosSearch(query, TagMode.AnyTag, query, 40, 1);

    ViewData["query"] = query;
    ViewData["photos"] = photos;
    
    
    if(ajax.HasValue && ajax.Value)
    {
        RenderView("_images", photos);
    }
    else
    {
        RenderView("results");    
    }       
}

We’ve added an optional parameter for the action to check for.  There are a number of other ways you might accomplish the same thing by embedding this into the controller itself.  You could make a convention that allows a base Controller class set an IsAjax boolean property to true if it detects this variable in form variables or querystring.  Whatever you find the cleanest.

And, Here is our new form!

Photo-search-ajax

You couldn’t see it in that still image, but there was an animated ajax indicator, and a nice slide down effect on the photo div.  If you turn off javascript, the whole thing still works.

This entire process is basically what you get with UpdatePanel.  The same techniques are used to accomplish partial page rendering.  Granted, UpdatePanel works slightly different (it’s parts of the same page that get updated) but you could easily return a full html document from the action and snip out the html you want to update.  Or you might have a specific ViewFactory that knows to only serialize HTML starting with an element root.

With some conventions and a tiny bit of javascript, you could easily achieve partial page rendering with out any fuss.  Plus you get to work with jquery, which is much mor enjoyable than ASP.NET AJAX.

UPDATE:  Download the code below.  You’ll need your own flickr api key (you can get one instantly from flickr.com).  Place it in the web.config.

PhotoSearch.zip (124 KB)

Saturday, February 23, 2008

ASP.NET MVC in Action - Early Access PDFs

book coverThe cat is out of the bag now!  I’m writing a book on ASP.NET MVC for Manning Press entitled ASP.NET MVC in Action.  I really thrilled to be part of this along with my co-authors Jeffrey Palermo and Dave Verwer.

So far 3 chapters are available on Manning’s Early Access Program.  We’d love to hear your feedback on the book so far.

Writing a book is much harder than I originally thought, but I think that blogging for four years has definitely helped.  With a book, everything has to be much more “polished,” so finishing up chapters tends to take a while.  This might explain my drop in posting frequency, but I hope to add tidbits of information that I’m working on to give you guys a peek at what the book will be like.

Our target release date will be October 2008.

Wednesday, January 30, 2008

Testing TempData in ASP.NET MVC

Eventually when testing your controllers you will come across an action that sets TempData.

If you didn’t know, TempData is a session-backed temporary storage dictionary that is available for one single request.  It’s great to pass messages between controllers.

Our Controller classes that we derive from have this as a member property.  However, when testing the controller, this always came up as null.

Reflector is our friend here, so I fired it up and loaded the Controller class.  Then I located the TempData member, and looked at the setter.  It’s marked as internal, so somebody inside this assembly is responsible for creating it.  By using the Reflector Analyze feature, I was able to find exactly where the setter is called.  It’s in the Execute() method.  (this is the general “pipeline” method that all IController implementations must override)

Think about that for a second.  At runtime, the TempData dictionary is set because all requests flow through Execute().  But in my test, I am specifically calling my action method.  There is no Execute() in my test.

To top it off, I can’t set this guy to a new TempDataDictionary because the setter is marked as internal.

Phil Haack tells me they’re working on this issue so we’ll likely see an improvement soon.  In the meantime you can get around the issue by using reflection to set the value.  But first, can we create one?

TempData is stored in session, so it needs a reference to IHttpContext.  Thankfully, we can mock this, however we have to make sure and mock the right parts.

First, create a mock for IHttpContext, create the return values for mocked IHttpRequest & IHttpResponse.  Finally setup return values for IHttpSession as well.  I’ve done all of this in a helper method of my test (though it could be an extention method on the MockRepository ala Phil, but whatever you like).

private IHttpContext GetHttpContext(string requestUrl)
{            
	IHttpRequest request = _mocks.DynamicMock<IHttpRequest>();
	SetupResult.For(request.Url).Return(new Uri(requestUrl));
	IHttpResponse response = _mocks.DynamicMock<IHttpResponse>();
	IHttpSessionState session = _mocks.DynamicMock<IHttpSessionState>();
        IHttpContext httpContext = _mocks.DynamicMock<IHttpContext>();

	SetupResult.For(httpContext.Session).Return(session);
	SetupResult.For(httpContext.Request).Return(request);
	SetupResult.For(httpContext.Response).Return(response);
        _mocks.Replay(request);
        _mocks.Replay(response);
        _mocks.Replay(httpContext);

_tempData = new TempDataDictionary(httpContext);
SetupResult.For(session[null]).IgnoreArguments().Return(_tempData);
_mocks.Replay(session);

 return httpContext; }

This code is a little verbose, but this is something you write once and use in all of your tests. You should notice that the _tempData property is a local field on my test class. I can then take this _tempData variable and inject it (via reflection) onto the controller under test. Here's how:

typeof(FooController).GetProperty("TempData").SetValue(controller, _tempData, null);

And now we can easily test that messages are set in TempData before redirecting or rendering a view.

*update*:  Will Shaver notified me of a typo in the code.  It has been fixed.  Thanks Will!*