So last week the guys and I at work started to spike ASP.NET MVC. We're starting up a new project, and decided to take advantage of the Preview 2 version of the so far released libraries. Our experiences so far have been.... hmmm... not as expected.

Here's a few things we've learned, hopefully they help someone else out. We're nant junkies, so the first thing we did to get going was automate the compiling, testing, running, deploying, and creation of the database with nant. We found that when running our project against the aspnet_compiler.exe that it didn't recognize some of the new C# 3.0 syntax.

1 <select name="protocolName">
2       <&#37; foreach( var dto in ViewData ) {&#37;>
3           <option><&#37;= dto.ProtocolName &#37;></option>
4       <&#37; } &#37;>
5   </select>

The above code would raise an error with the aspnet_compiler.exe. Now this is valid C# 3.0, but the pre compiler didn't know what to do with the "var" keyword. Next, the precompiler didn't know where to find the "Form()" method on the Html helper class because, it's an extension method.

1 <&#37; using( Html.Form( Controllers.Order.Name, "submit", FormMethod.Post ) ) {&#37;>

It's kind of an interesting idea that so many methods on the "HtmlHelper" class are extension methods. The solution to getting the aspnet_precompiler to recognize the C#3.0 syntax was to dump this block of xml in the web.config.

1 <system.codedom>
2       <compilers>
3           <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" 
4               type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
5               <providerOption name="CompilerVersion" value="v3.5"/>
6               <providerOption name="WarnAsError" value="false"/>
7           </compiler>
8       </compilers>
9   </system.codedom>

Next up... testing controllers. I think the guys and I were a little surprised at just how awkward it was to test a controller. I thought, a lot of time was spent making the controllers more testable. Our first pain point was the fact that "RenderView()" is a protected method on the Controller base class. Here's what I'm talking about...

 1 public class HomeController : Controller
 2   {
 3       public void Index( )
 4       {
 5           RenderView( "Index" );
 6       }
 7 
 8       public void About( )
 9       {
10           RenderView( "About" );
11       }
12   }

So let's think... how can we test that when the Index action is invoked it calls "RenderView" with an argument value "Index"... So some people have suggested creating a Test Double. I say... booo... I use mock object frameworks so that I don't need to groom a garden of hand rolled test stubs. Here's what we came up with... first cut remember!

 1 public class OrderController : BaseController, IOrderController
 2   {
 3       private readonly IOrderIndexCommand indexCommand;
 4       private readonly ISubmitOrderCommand submitCommand;
 5   
 6       public OrderController( IOrderIndexCommand indexCommand, ISubmitOrderCommand submitCommand )
 7       {
 8           this.indexCommand = indexCommand;
 9           this.submitCommand = submitCommand;
10       }
11   
12       public void Index( )
13       {
14           indexCommand.InitializeWith( this );
15           indexCommand.Execute( );
16       }
17   
18       public void Submit( )
19       {
20           submitCommand.InitializeWith( this );
21           submitCommand.Execute( );
22       }
23   }

Ok... so it's slightly more testable. Each action on the controller executes a command, after first being initialized with ... The other thing to notice is that the OrderController inherits from BaseController. BaseController is actually an adapter that implements an IViewRenderer interface.

1 public abstract class BaseController : Controller, IViewRenderer
2   {
3       public void Render< TypeToBindToView >( IView view, TypeToBindToView viewData )
4       {
5           RenderView( view.Name( ), viewData );
6       }
7   }

The OrderIndexCommand is initialized with an IViewRenderer.

 1 public class OrderIndexCommand : IOrderIndexCommand
 2   {
 3       private IViewRenderer viewEngine;
 4       private readonly IOrderTasks task;
 5   
 6       public OrderIndexCommand( IOrderTasks task )
 7       {
 8           this.task = task;
 9       }
10   
11       public void InitializeWith( IViewRenderer engineToRenderViews )
12       {
13           viewEngine = engineToRenderViews;
14       }
15   
16       public void Execute( )
17       {
18           viewEngine.Render( ControllerViews.Order.Index, task.RetrieveAllProtocols( ) );
19       }
20   }
comments powered by Disqus