Jul 25 2010

Starting smtp4dev Automatically

Category: MVCMatt @ 06:35

smtp4dev is a great little tool for testing E-mail sending functionality in your application.  Instead of setting up your own SMTP server and fighting your ISPs restrictions, smtp4dev sits in your system tray and acts like a mock SMTP server, catching instead of relying them on to their destination.  You can then view the messages in the E-mail client of your choice.  Unfortunately, developers have to remember to run smtp4dev before they start testing the application.  If you are building an ASP.NET application, here’s a little trick that you can use to start smtp4dev when your application starts up.  NOTE: Big thanks to Rob for suggesting this.

RageFeed has the following folder structure:

Assets
Databases
Libraries
Source
	...
	Web (our MVC application)
	...
Utilities
	smtp4dev.exe

When a developer launches the ASP.NET MVC application, we’d like for smtp4dev to be launched automatically (if it isn’t already running).  Initially I was trying to make this problem harder than it was, exploring ways to perhaps have Visual Studio launch the process, but Rob’s suggestion was to just throw it in in the Application_Start event: 

protected void Application_Start()
{
    ...

    if (ConfigurationManager.AppSettings["Environment"] == "Development")
    {
        try
        {
            Smtp4Dev.Start();
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException("Unable to start Smtp4Dev, check that AppSettings.Environment is set correctly.", ex);
        }
    }
}

This works, but you have to ensure that smtp4dev will only be launched when developers are testing things locally.  I added an AppSetting that contains the current deployment’s environment (ie: Development, Staging, Production, etc).  The app only attempts to run smtp4dev if the current environment is “Development”. 

The actual smpt4dev bootstrapper is quite simple:

public static class Smtp4Dev
{
    public static void Start()
    {
        if (Smtp4DevIsRunning())
        {
            return;
        }

        var path = HttpContext.Current.Server.MapPath("~/") + @"..\..\Utilities\smtp4dev.exe";

        Process.Start(path, "");
    }

    private static bool Smtp4DevIsRunning()
    {
        return Process.GetProcessesByName("smtp4dev.exe").Length > 0;
    }
}

Presto, when the application starts, it will launch smpt4dev if it isn’t already running.

Tags:

Jul 8 2010

Using MVCContrib glue to bring Visual Basic WebForms and C# MVC together

Category: ASP.NET | MVCMatt @ 16:14

While I’m digging my new job, I’ve found myself in the less-than-desirable situation of having to work with WebForms again.  After working almost exclusively with ASP.NET MVC for the better part of three years, the inadequacies of WebForms seem even more prominent.  While running MVC and WebForms in the same applications is easy enough, we’re facing an added complication: our WebForms application was written in Visual Basic.  While you can’t mix-and-match C# MVC and VB WebForms within the same application directly, you can leverage MVCContrib’s Portable Areas to bring the two together, which is what I’ll show you how to do in this post.

Giving credit where credit is due, Jeffery Palermo already described how you can leverage MVCContrib’s Portable Areas within a WebForms application.  Unfortunately, his sample doesn’t work (it appears to depend on an older MVC 2 CTP), and his post omits several important details.  Jeffery’s post also focuses on the creation of reusable modules whereas my interest is bringing C# into our VB project as seamlessly as possible.  Hopefully this guide, even though it’s focus is on bridging the C#-VB divide, will be useful to anyone that’s envious of MVC but stuck with a substantial amount of WebForms code.

Create a new solution

To get started, let’s go ahead and create a new empty solution in Visual Studio 2010.

VisualStudioNewProject

Next, add a Visual Basic WebForms application.  When you run the application, you should see something like this:

VbWebFormsHome

Note that I modified my master page to make it clear that the master page is a WebForms master page.  You’ll understand why later. 

Add MVC Support

As I mentioned, there’s plenty of information on the web about adding MVC support to an existing WebForms application.  The two play nicely side-by-side in the same project.  First, add references to the required assemblies: System.Web.Mvc, System.Web.Routing, and System.Web.Abstractions.  The last one is probably not strictly required, but I added it anyway. This is also a good time to go ahead and add a reference to MVCContrib.  Note that for the approach I’m going to describe to work, you will need to use a build based on the trunk and not the older release build available from CodePlex.  You can pick up a nightly build from the MVCContrib TeamCity build server (be sure to grab artifacts from a successful build).

MvcContribTeamCity

Next, you need to update your web.config file.  Replace the default compilation section with the following:

<compilation debug="true" targetFramework="4.0">
    <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, 
             Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, 
             Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Mvc, Version=2.0.0.0, 
             Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
</compilation>

Also add a pages element with the namespaces shown:

<pages>
    <namespaces>
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Ajax"/>
        <add namespace="System.Web.Mvc.Html"/>
        <add namespace="System.Web.Routing"/>
    </namespaces>
</pages>

This step probably isn’t necessary (depends on how you are deploying your application), but for good measure, go ahead and add the MVC HTTP handler and a binding redirect for MVC 1:

<httpHandlers>
    <add verb="*" path="*.mvc" 
        validate="false" type="System.Web.Mvc.MvcHttpHandler,
        System.Web.Mvc, Version=2.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
            <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
            <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
        </dependentAssembly>
    </assemblyBinding>
</runtime>

Next up, we need to add some code in our Global.asax file:

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    ' MapRoute takes the following parameters, in order:
    ' (1) Route name
    ' (2) URL with parameters
    ' (3) Parameter defaults
    routes.MapRoute( _
     "Default", _
     "{controller}/{action}/{id}", _
     New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional} _
    )

End Sub

Sub Application_Start()
    AreaRegistration.RegisterAllAreas()

    PortableAreaRegistration.RegisterEmbeddedViewEngine()

    RegisterRoutes(RouteTable.Routes)
End Sub

The RegisterRoutes procedure is pretty standard, and AreaRegistration.RegisterAllAreas is boiler-plate MVC 2.0 stuff.  The other bit is important though: PortableAreaRegistration.RegisterEmbeddedViewEngine will register a special view engine that allows the application to consume Portable Areas.

The final thing we need to do is add some special folders that are needed by MVC: the Areas folder and the Views folder.  For now, go ahead and add a view master page at ‘~/Views/Shared/Site.master’.  I’d recommend copying both the Views and Areas folders from an existing ASP.NET MVC application (be sure to remove any of the views you don’t need, but save the Site.master one).  Once you’re finished, you should have a structure like this:

VbProjectCurrentState

At this point, the WebForms project is ready to consume our C# ASP.NET MVC Portable Area.  All we need to do is create one!

Creating a Portable Area

Most people recommend that you create a regular class library to serve as the host of your portable area, but there are some disadvantages to that approach.  First, you will have to re-add all the necessary MVC references in order to build controllers and views.  Second, you will lose all the Visual Studio tooling for MVC (as well as Resharper extensions for MVC projects).  In light of that, I recommend adding a regular-old ASP.NET MVC 2 (C#) application to your solution.  An MVC project is essentially a web application project, which means it produces a single DLL that you can reference from another application. 

After adding your MVC 2 project, you can remove all of the following: web.config files, Global.asax, views (but leave ‘~/Views/Shared/Site.master’ for now), controllers, models, script files, and CSS files. Save your project at this point, then unload it so that you can edit it. 

Edit project file, and add the following line to end of script before closing ‘project’ tag:

<Target Name="BeforeBuild">
  <ItemGroup>
    <EmbeddedResource Include="**\*.aspx;**\*.ascx;**\*.gif;**\*.css;**\*.js;**\*.png;**\*.jpg" />
  </ItemGroup>
</Target>

One of the requirements for building a Portable Area is that the views and any resources they depend on must be embedded in the assembly.  You can do this manually using the Properties pane in Visual Studio, but it’s easy to forget this critical step.  The MSBuild snippet above automatically embeds your views and any common content files. 

With your project file now modified, go ahead and reload it, then add a reference to MVCContrib.  We’ll add a “HelloWorld” controller to our project:

public class HelloWorldController : Controller
{
    public ViewResult Index()
    {
        return View();
    }

}

And we’ll create the corresponding view for our Index action:

<%@ Page Title="Hello from C#!" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Hello from C#!
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Hello, World!</h2>
    <p>This is a view from a portable area created in C#!</p>
    <p>The current time is <%=DateTime.Now.ToShortTimeString() %></p>

</asp:Content>

When creating the view, go ahead and specify the master, but note that at runtime the view will actually use the master from the parent project, not the Portable Area project.  We left the master view in the Portable Area project so that the views we create will have the correct content placeholder IDs (more on this later). 

The last thing we need to do to complete our portable area is add a registration class:

public class HelloWorldRegistration : PortableAreaRegistration
{
    public override string AreaName
    {
        get { return "HelloWorld"; }
    }

    public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
    {
        context.MapRoute("HelloWorld", "helloworld/{action}",
                         new { controller = "helloworld", action = "index" });

        RegisterAreaEmbeddedResources();
    }
}

Registration is fairly simple: we give our area a name, map a route to it, and call a base class method to register all the embedded resources for our Portable Area.

Visual Basic: Say Hello to C#!

So far we have a Visual Basic WebForms application and  C# MVC application, but the two aren’t playing in the same sandbox yet.  In the WebForms application, add a project reference to the C# MVC application.  Visual Studio will kindly let us do this because an MVC project is really just a class library project underneath all the bells and whistles.  

Build and launch your WebForms app.  It should take you to Default.aspx, which is a regular WebForms page.  Now, try navigating to the controller we created in C# by appending “/helloworld” to the URL.  If I’ve described the steps correctly, you should be greeted with something like the following:

CSharpPortableArea1

Our WebForms app is routing requests to the controller we defined in C#, and the embedded view engine is returning the corresponding view.  Do note, though, that our view is being rendered using our VB MVC view master page, and not the view master page from our C# Portable Area.  That’s by design: Portable Areas are designed to be plugged in and reused within an application.  In this case though, we’d actually like for our C# view to use the same WebForms master page as our existing WebForms content; we don’t want to maintain two distinct master pages.  It turns out that you can do this quite easily.

C# View, Meet VB WebForms Master Page

Examining our view again, we see that it provides content for two placeholders: TitleContent and MainContent.  These placeholders match the standard MVC view master page that we added to our WebForms project under ‘~/Views/Shared/Site.Master’.  If you open up the WebForms master at ‘~/Site.master’ though, you’ll notice that it defines only HeadContent and MainContent.  Let’s add a TitleContent placeholder so that our views can still define their own page titles in the usual MVC way:

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server"/></title>
    <link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server">
    </asp:ContentPlaceHolder>
</head>

Next, go back our view in the Portable Area, and change its MasterPageFile property to ‘~/Site.master’.  If you are using Resharper, you’ll notice errors indicating that there’s no master page file at that location and that your content placeholder IDs are no longer valid.  That’s because as far as Resharper (and Visual Studio) know, you are now referencing a master that doesn’t exist.  The only view master page in the C# project is at ‘~/Views/Shared/Site.master’.  All we need to do is move the master from it’s current location to the root of our C# project, and the errors will go away.  When we create new views in the future, we can reference the view master page at its new location, and our views will be generated with the correct path for our WebForms master.

Rebuild and refresh your application, and you’ll see that the Index view from our HelloWorld controller is now being rendered within the WebForms master, title-and-all:

CSharpPortableArea2

Keep in mind that what I’ve described is a proof-of-concept only; I have not tried applying this to a real project yet, and there are likely to be issues with this approach.  If you do run into issues, I’d like to hear about them.  If you want to checkout my code, you may download the sample project here.

Tags:

Apr 27 2010

Using Moq Dynamically

Category: MVCMatt @ 14:45

The TestControllerBuilder in MVCContrib’s TestHelper library has a hard dependency on Rhino Mocks.  I’m not a big fan of Rhino Mocks, I prefer to use Moq, and while it’s not a big deal to reference two separate mocking libraries, I’d rather just have one mocking library in my project.  I’m working on a patch to address this open workitem in MVCContrib.  I don’t want to create a hard reference to Moq, so that means I have to access Moq, and all it’s generic types and methods, using reflection.  This post presents my Moq proxy class, based on a similar idea from StructureMap’s auto-mocking container.

The TestControllerBuilder in MVCContrib.TestHelper is a handy class for attaching mocks to an MVC Controller class to facilitate testing.  Unfortunately, it uses Rhino Mocks, which means you must include Rhino Mocks in your projects in order to leverage it.  My test projects already reference Moq (my preferred testing framework), and I don’t like the idea of carrying around two libraries that provide the same capabilities.  I’m not the only one, as there has been an open work item to address this issue for quite some time now. One of the nice things about Open-Source Software is that instead of wasting energy complaining about something, I can try to fix it. 

I have three main goals for the fix.  First, it should not be a breaking change.  Existing consumers of the API should be unaffected.  Second, it should not create a hard reference between MVCContrib and Moq.  MVCContrib is already using Rhino Mocks for its own testing, and I’m trying to remove the need for referencing two mocking frameworks in my own projects, so it would be a bit ironic if I required MVCContrib to pick up a dependency on Moq.  Finally, I want it to be very simple to swap out the mocking library; in fact, I’d like it to automatically use whichever library is available.  I’d even like it to be pluggable so that a different mocking library could be plugged in if desired.

I’m not 100% tied to this approach yet (I’m still thinking of this more as a spike for now), but I’m using the strategy pattern to decouple TestControllerBuilder from the mocking library.  There are other places in MVCContrib’s TestHelper library that are doing mocking, so I will probably change my approach, but this worked well as a proof-of-concept.  TestControllerBuilder now accepts an IControllerBuilderStrategy that handles the actual mocking and any other library-specific setup.  The interface is quite simple:

public interface IControllerBuilderStrategy 
{
    void Setup(TestControllerBuilder testControllerBuilder);
}

By default, TestControllerBuilder uses the RhinoMocksControllerBuilder (this satisfies my first goal: existing consumers of the API will get the exact same behavior as before).  Because MVCContrib.TestHelper has a direct dependency on Rhino Mocks, RhinoMocksControllerBuilder is a straight-forward refactoring of TestControllerBuilder’s mock-dependent logic into a new class:

public class RhinoMocksControllerBuilder : IControllerBuilderStrategy 
{
    protected MockRepository _mocks = new MockRepository();

    public void Setup(TestControllerBuilder testControllerBuilder)
    {
        var httpContext = _mocks.DynamicMock<HttpContextBase>();

        var request = _mocks.DynamicMock<HttpRequestBase>();
        var response = _mocks.DynamicMock<HttpResponseBase>();
        var server = _mocks.DynamicMock<HttpServerUtilityBase>();
        var cache = HttpRuntime.Cache;

        SetupResult.For(httpContext.Request).Return(request);
        SetupResult.For(httpContext.Response).Return(response);
        SetupResult.For(httpContext.Session).Return(testControllerBuilder.Session);
        SetupResult.For(httpContext.Server).Return(server);
        SetupResult.For(httpContext.Cache).Return(cache);

        SetupResult.For(request.QueryString).Return(testControllerBuilder.QueryString);
        SetupResult.For(request.Form).Return(testControllerBuilder.Form);
        SetupResult.For(request.AcceptTypes).Do((Func<string[]>)(() => testControllerBuilder.AcceptTypes));
        SetupResult.For(request.Files).Return((HttpFileCollectionBase)testControllerBuilder.Files);

        Func<NameValueCollection> paramsFunc = () => new NameValueCollection { testControllerBuilder.QueryString, testControllerBuilder.Form };
        SetupResult.For(request.Params).Do(paramsFunc);

        SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Do(
            (Func<string>)(() => testControllerBuilder.AppRelativeCurrentExecutionFilePath));
        SetupResult.For(request.ApplicationPath).Do((Func<string>)(() => testControllerBuilder.ApplicationPath));
        SetupResult.For(request.PathInfo).Do((Func<string>)(() => testControllerBuilder.PathInfo));
        SetupResult.For(request.RawUrl).Do((Func<string>)(() => testControllerBuilder.RawUrl));
        SetupResult.For(response.Status).PropertyBehavior();
        SetupResult.For(httpContext.User).PropertyBehavior();

        _mocks.Replay(httpContext);
        _mocks.Replay(request);
        _mocks.Replay(response);

        testControllerBuilder.HttpContext = httpContext;
    }
}

Things get a little trickier for the Moq version.  Since TestHelper can’t reference Moq directly, it can’t use the API directly.  I decided to use the proxy pattern after reviewing how StructureMap’s AutoMocking Container handles a similar situation.  The builder itself is still very straight forward.  Though the proxy’s methods don’t map one-to-one to the underlying Moq API, they are pretty close:

public class MoqControllerBuilder : IControllerBuilderStrategy
{
    private static Exception _loadException;
    private static MoqProxy _proxy;

    static MoqControllerBuilder()
    {
        try
        {
            _proxy = new MoqProxy();
        }
        catch (Exception ex)
        {
            _loadException = ex;
        }
        
    }

    public void Setup(TestControllerBuilder testControllerBuilder)
    {
        if (_proxy == null)
        {
            throw new InvalidOperationException("Cannot use MoqControllerBuilder because an error occured while loading Moq.",
                                                _loadException);
        }

        var httpContext = _proxy.DynamicMock<HttpContextBase>();

        var request = _proxy.DynamicMock<HttpRequestBase>();
        var response = _proxy.DynamicMock<HttpResponseBase>();
        var server = _proxy.DynamicMock<HttpServerUtilityBase>();
        var cache = HttpRuntime.Cache;

        httpContext.ReturnFor(c => c.Request, request.Object);
        httpContext.ReturnFor(c => c.Response, response.Object);
        httpContext.ReturnFor(c => c.Session, testControllerBuilder.Session);
        httpContext.ReturnFor(c => c.Server, server.Object);
        httpContext.ReturnFor(c => c.Cache, cache);
        httpContext.SetupProperty(c => c.User);

        request.ReturnFor(r => r.QueryString, testControllerBuilder.QueryString);
        request.ReturnFor(r => r.Form, testControllerBuilder.Form);
        request.ReturnFor(r => r.Files, (HttpFileCollectionBase)testControllerBuilder.Files);
        request.CallbackFor(r => r.AcceptTypes, () => testControllerBuilder.AcceptTypes);
        request.CallbackFor(r => r.Params, () => new NameValueCollection { testControllerBuilder.QueryString, testControllerBuilder.Form });
        request.CallbackFor(r => r.AppRelativeCurrentExecutionFilePath, () => testControllerBuilder.AppRelativeCurrentExecutionFilePath);
        request.CallbackFor(r => r.ApplicationPath, () => testControllerBuilder.ApplicationPath);
        request.CallbackFor(r => r.PathInfo, () => testControllerBuilder.PathInfo);
        request.CallbackFor(r => r.RawUrl, () => testControllerBuilder.RawUrl);
        response.SetupProperty(r => r.Status);

        testControllerBuilder.HttpContext = httpContext.Object;
    }
}

The *real* fun happens in the proxy class. First is the MoqProxy class, which handles dynamically loading the Moq API and, using reflection, creating mock objects:

public class MoqProxy
{
    private Type _mockOpenType;

    public MoqProxy()
    {
        Assembly Moq = Assembly.Load("Moq");
        _mockOpenType = Moq.GetType("Moq.Mock`1");

        if (_mockOpenType == null)
        {
            throw new InvalidOperationException("Unable to find Type Moq.Mock<T> in assembly " + Moq.Location);
        }
    }


    public MockProxy<T> DynamicMock<T>()
    {
        return new MockProxy<T>(_mockOpenType.MakeGenericType(typeof(T)));
    }
}

The MockProxy<T> class is the proxy for the underlying Mock<T> type, which is the class used for setting up expectations.  This was a tricky class to implement due to the heavy use of generic parameters, but in the end, I managed to get it working:

public class MockProxy<T>
{
    private readonly Type _mockType;
    private PropertyInfo _objectProperty;
    private object _instance;

    public T Object 
    { 
        get
        {
            return (T)_objectProperty.GetValue(_instance, null);
        }
    }

    public MockProxy(Type mockType)
    {
        _mockType = mockType;
        _instance = Activator.CreateInstance(_mockType);
        _objectProperty = mockType.GetProperty("Object", _mockType);
    }

    private MethodInfo GetSetupMethod<TResult>() {
        var openSetupMethod = _mockType.GetMethods().First(m => m.IsGenericMethod && m.Name == "Setup");
        return openSetupMethod.MakeGenericMethod(typeof(TResult));
    }

    public void ReturnFor<TResult>(Expression<Func<T, TResult>> expression, TResult result)
    {
        var setupMethod = GetSetupMethod<TResult>();
        var setup = setupMethod.Invoke(_instance, new object[] { expression });
        var returnsMethod = setup.GetType().GetMethod("Returns", new [] {typeof(TResult)});
        returnsMethod.Invoke(setup, new object[] { result});
    }

    public void CallbackFor<TResult>(Expression<Func<T, TResult>> expression, Func<TResult> callback)
    {
        var setupMethod = GetSetupMethod<TResult>();
        var setup = setupMethod.Invoke(_instance, new object[] { expression });
        var returnsMethod = setup.GetType().GetMethod("Returns", new[] { typeof(Func<TResult>) });
        returnsMethod.Invoke(setup, new object[] {callback});
    }

    public void SetupProperty<TProperty>(Expression<Func<T, TProperty>> expression)
    {
        var openSetupMethod = _mockType.GetMethods().First(m => m.Name == "SetupProperty" && m.GetParameters().Length == 1);
        var setupMethod = openSetupMethod.MakeGenericMethod(typeof(TProperty));
        setupMethod.Invoke(_instance, new object[] {expression});
    }
}

Reflection is somewhat limited when it comes to working with overloaded generic methods.  I ended up using this approach for finding the desired overload via LINQ. 

With all the pieces in place, the TestControllerBuilder unit tests now pass when using either Rhino Mocks or Moq.  Again, this isn’t my final solution, I’m still waiting on feedback on a few possible approaches to decide what’s going to actually be implemented, but the MoqProxy is probably going to be a key piece of whatever solution I move forward with.

Tags:

Mar 12 2010

Client-Side Validation with ASP.NET MVC 2.0 Futures

Category: ASP.NET | MVCMatt @ 16:26

Just a quick post to hopefully save others some time.  ASP.NET MVC 2.0 has built-in support for client-side validation, but it doesn’t work with the expression-based BeginForm HtmlHelper methods that are available in the ASP.NET MVC Futures assembly.  I also don’t like that I have to call a separate helper method to set my form up for validation.  To address these two limitations, I wrote my own extension method:

public static class ValidationHelper
{
    private const string LAST_FORM_ID_KEY = "_lastFormId";

    public static MvcForm BeginValidatedForm<TController>(this HtmlHelper helper, Expression<Action<TController>> action)
            where TController : Controller
    {
        helper.EnableClientValidation();

        var id = GetNextFormId(helper);

        TagBuilder builder = new TagBuilder("form");
        string str = helper.BuildUrlFromExpression(action);
        builder.MergeAttribute("action", str);
        builder.MergeAttribute("method", "POST");
        builder.GenerateId(id);
        helper.ViewContext.HttpContext.Response.Write(builder.ToString(TagRenderMode.StartTag));

        var form = new MvcForm(helper.ViewContext);

        helper.ViewContext.FormContext.FormId = id;

        return form;
    }

    private static string GetNextFormId(HtmlHelper helper)
    {
        int count = (int) (helper.ViewContext.HttpContext.Items[LAST_FORM_ID_KEY] ?? 0);
        count++;

        helper.ViewContext.HttpContext.Items[LAST_FORM_ID_KEY] = count;

        return string.Format("form{0}", count);
    }
}

Now you can simply do like this:

<h1>Sign-up</h1>

<p>Signing up is easy <em>and</em> free!</p>

<% using (Html.BeginValidatedForm<SignupController>(c => c.Create(null)))
{ %>
    <fieldset>
        <div>
            <p>1) Choose a username:</p>
            <%=Html.EditorFor(m => m.Username) %><%=Html.ValidationMessageFor(c => c.Username) %>
        </div>
        <div>
            <p>2) What's your E-mail address? <span class="note">(<a href="#">Why do we need this?</a>)</span></p>
            <%=Html.EditorFor(m => m.Email)%><%=Html.ValidationMessageFor(c => c.Email) %>
        </div>
        <%=Html.SubmitButton("submit", "Start Raging Now!") %>
    </fieldset>
<%} %>

and get client-side validation (assuming you’ve set everything else up correctly).

Tags:

Dec 22 2009

Exposing the View Model to JavaScript in ASP.NET MVC

Category: MVC | JavaScriptMatt @ 17:24

The prevailing practice for moving data between the controller and the view in ASP.NET MVC applications is to utilize a view model.  While using a view model from within the view’s ASPX page is quite easy, utilizing it from JavaScript can be more complex.  While JavaScript blocks declared inline on the view page can easily consume values from the model, external script files cannot.  In order to take advantage of script batching and minimization, you should avoid the use of inline script blocks and instead use external JavaScript files (.js).  What happens when you need to reference a value from the view model in your JavaScript though?  Since the JavaScript files are not (by default) processed by the ASP.NET pipeline, it isn’t possible for them to leverage the Model; the Model exists server-side, while JavaScript is processed client-side. 

I’ve struggled with this limitation since the first preview release of ASP.NET MVC.  Here are a couple of the approaches that I tried (and hated):

Place scripts in partial views

Instead of following best-practices and placing JavaScript in an external script file, scripts can instead be placed in partial views.  This simplifies the main view by encapsulating the script, and it does allow the script to be re-used.  It also allows the script to easily reference values from the view model. 

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AwesomeViewModel>" %>

<script type="text/javascript">
    
    alert('Hello from the view model: <%=Model.Hello%>');
    
</script>

The downside to this approach is that the script cannot be easily minimized or combined, and it can’t be cached by the browser since it is actually rendered inline in the final markup produced by the view. 

Pass view model properties through an initialization function

Another approach that I’ve used is to define an initialization function within my external JavaScript files.  Any values that are needed by the script can be extracted from the model within the main view, then passed to the external JavaScript through the initialization function.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AwesomeViewModel>" %>
...
<script type="text/javascript" src="ExternalScript.js"></script>
<script type="text/javascript">
    var name = "<%=Model.Name %>";
    ExternalScript_Init(name);
</script>
...

//Contents of ExternalScript.js
ExternalScript_Init(name) {
    alert("Hello from JavaScript: " + name);
}

This approach is also less than ideal.  Any values the script requires must be manually extracted from the view model, which makes maintenance more of a headache than it should be. 

The ideal solution

The previous two approaches “work”, but they each have drawbacks.  The ideal solution would allow the model to be easily consumed by external JavaScript files within a minimal amount of manual work.  Adding a new property to the view model should require zero JavaScript in order to expose the new property for use by scripts.  It turns out that this is actually quite easy to do….

The right way: serialize the model to JavaScript!

.NET 3.5 introduced the JavaScriptSerializer class for serializing objects to/from JSON. With it, most .NET types can be easily converted into a form that’s easily consumable by JavaScript.  Scott Gu introduced a simple ToJSON extension method on his blog which can be used to transform a view model into JSON.  When the output of this method is assigned to a JavaScript variable, the properties of the view model effectively become available to client-side script (note that methods defined on the view model, if any, are ignored by the JavaScriptSerializer). 

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AwesomeViewModel>" %>
...
    <script type="text/javascript">
        var Model = <%=Model.ToJson() %>
        //If needed, new properties can be added to the model, such as URLs for AJAX requests:
        Model.MyAjaxMethod = '<%=Html.BuildUrlFromExpression<MyController>(c => c.DoAjaxyThing()) %>';        
    </script>

The above code block should appear before any scripts that wish to access properties from the view model, which can reference view model properties easily:

<script type="text/javascript">
    
    alert("Hello from the JSON view model:" + Model.Hello);
    
</script>

This is a big improvement over the other two approaches I’ve tried (at least in my opinion), but it still requires me to perform this mundane serialization task on each view.  An easy improvement is to move it to the master page:

<script type="text/javascript">
    var Model = <%=Model != null ? Model.ToJson() : "{}" %>;
</script>

Now every view that has a view model will automatically have a Model object available.

Thoughts?  Suggestions?  Anyone found a better solution that I’ve just overlooked somehow?

Tags:

Dec 11 2009

My best (or worst) MVC hack to date&hellip;

Category: MVCMatt @ 06:40

I really don’t know if I should be proud or embarrassed by what I just implemented.  I’m going to go with “embarrassed”.  If anyone sees a better way to do it, let me know.

The Problem

I have a multi-tab interface (via jQuery UI  tabs).  The view is strongly-typed and has a view model, so it’s decoupled from the domain objects that are used to populate it.  The view is rendered using the MVC Future expression-based input builders, so instead of Html.TextBox(“Blah”), I’ve got Html.TextBoxFor(m => m.Property).  For those that don’t know, the expression-based builders allow you to bind to your view model using expressions instead of magic strings, so you get compile-time safety, rename support, etc.  Anyway, two of the tabs are *almost exactly* the same except for their labels and the underlying object that they’re bound to.  Basically, the original view model looks like this:

public class MySuperViewModel
{
    WidgetViewModel Tab1 { get; set; }
    
    WidgetViewModel Tab2 { get; set; }
}

What I want to do is render the same UI for both FirstTab and SecondTab, have both wired up by ASP.NET automagically, and not have to repeat a bunch of markup.

What doesn’t work

My first though was “oh, I’ll just make a strongly-typed partial view for WidgetTabViewModel, and render it twice!”  That doesn't work though because of how the expression-based input builders work.  They build their IDs based on the expression you pass in.  If I was going to take this approach, inside the partial for the tabs I would be calling this ‘Html.TextBoxFor(m => m.WidgetName)’, which will generate the following markup:

<input name=”WidgetName” … ></input>

Note that there’s nothing in the markup that’s going to let it ASP.NET MVC know where to wire things up in relation to the original view model.  Not good. This is what it needs to look like:

<input name=”Tab1_WidgetName” …></input>

My next thought was “well, I’ll just make a special view model to drive the tab partial view, and pass the expressions in via the view model”, like so:

public class TabViewModel
{
    public Expression<Func<MySuperViewModel, string>> Name;
    
    public Expression<Func<MySuperViewModel, string>> Value;
}

...
//Does not compile, TextBoxFor is actually TextBoxFor<Expression<Func<TabViewModel,object>>
<%=Html.TextBoxFor(Model.Name) %> 

This almost works, but unfortunately the expression-based input builders are defined on HtmlHelper<TModel>, where TModel is your view model type, meaning that the expressions I passed in from the top-level view do not match what TextBoxFor expects as input. 

My horrendous hack/brilliant solution

The fundamental problem is that I want to bind my inputs to expressions as if they were built from the top-level view using its HtmlHelper.  My solution is simple: I just pass in the top-level view’s HtmlHelper view the view model:

public class TabViewModel
{
    public Expression<Func<MySuperViewModel, string>> Name;
    
    public Expression<Func<MySuperViewModel, string>> Value;
    
    //HOT (Hack Or Not)?
    public HtmlHelper<MySuperViewModel> Helper;
}

In the partial view, I can render my inputs and get the correct binding names for MVC using ‘<%=Model.Helper.TextBoxFor(Model.Name))’.  In the master view, I can render the partial view using something like this:

<% Html.RenderPartial("MyTab", new TabViewModel
{
    Name = m => m.Tab1.Name,
    Value = m => m.Tab1.Value,
    Label = "Tab 1",
    Helper = Html
}); %>

I was sort-of surprised when this worked, but it did, and it saved me from having to duplicate about 100 lines of markup.  It still feels dirty though, so if anyone sees a good way to get around this (and I really hope there is), let me know. 

Tags:

Dec 1 2009

A fluent HtmlHelper extension for using FusionCharts in ASP.NET MVC, Part 2

Category: ASP.NET | MVCMatt @ 08:44

Hey, it only took me nearly a month to write part 2 of this series!  Yeah, I’ve been neglecting this blog a lot lately.  There just aren’t enough hours in the day to write. 

In part 1, I discussed charting with ASP.NET MVC and why I decided to use a Flash solution (FusionCharts Free) instead of an ASP.NET control or a JavaScript solution.  Nearly a month later, I’m still very glad I made the switch. 

As I said in part 1, FusionCharts Free includes some methods for working with FusionCharts from ASP.NET, and those work fine in both WebForms and MVC applications.  They aren’t very MVC-like though, and they take quite a few parameters that I don’t want to deal with most of the time.  They also don’t help with building the XML that configures a FusionChart.  The markup the methods generate also suffers from the annoying Flash z-order glitch.  To overcome these limitations, I decided to create some HtmlHelper extensions.  I wanted something that was flexible yet simple.  While I think that fluent APIs have been abused by the .NET community, I do think there are times where they make sense.  When using an HtmlHelper extension, I want to be able to configure the extension without resorting to a code block, which is exactly why I chose to go the fluent route for the FusionCharts helpers.  Before digging into the implementation, let’s take a look at the API in action again.  This is a simple example using an array of numbers, but the API actually supports any object you want to work with:

<%=Html.FusionCharts().Column2D(new[] {1, 2, 3, 4, 5}, 300, 300, d => d)
        .Caption("Numbers")
        .SubCaption("(subcaption)")
        .Label(d => "Label " + d)
        .Hover(d => "Hover " + d)
        .Action(d => "javascript:alert(&apos;You clicked on " + d + "&apos;);")%>

This renders a 2D bar chart, like so:

barChart

All of the methods should be fairly self-explanatory, except perhaps the Action method.  Action allows you to create hyperlinks for the data items in your chart.  In this case, I’ve created a JavaScript link that will display an alert when a bar in the chart is clicked. 

As I said, this API is generic and supports any type.  This allows you to pass in complicated objects that expose values, labels, etc. that can be bound using the various methods exposed on the fluent API. 

Time for some code.  First is the HtmlHelper extension and the entry point to the FusionChart helpers:

/// <summary>
/// Container for the actual extension method.
/// </summary>
public static class FusionChartsHtmlHelper
{
    /// <summary>
    /// Gets a helper for building a fusion chart.
    /// </summary>
    /// <param name="helper"></param>
    /// <returns></returns>
    public static FusionChartsHelper FusionCharts(this HtmlHelper helper)
    {
        return new FusionChartsHelper(helper);
    }
}

/// <summary>
/// An HTML helper for FusionCharts. 
/// </summary>
public class FusionChartsHelper
{
    /// <summary>
    /// The HTML helper.
    /// </summary>
    private readonly HtmlHelper mHtmlHelper;

    /// <summary>
    /// The resolved path to the Fusion Charts SWF files.
    /// </summary>
    private readonly string mChartsFolderBase;

    /// <summary>
    /// Initializes the helper. 
    /// </summary>
    /// <param name="helper"></param>
    public FusionChartsHelper(HtmlHelper helper)
    {
        mHtmlHelper = helper;
        UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

        mChartsFolderBase = urlHelper.Content("~/Charts/");
    }

    /// <summary>
    /// Gets a fusion chart builder that will create a 2D bar chart.
    /// </summary>
    /// <typeparam name="T">The type of the data items.</typeparam>
    /// <param name="data">The items to bind to the chart.</param>
    /// <param name="width">Width in pixels.</param>
    /// <param name="height">Height in pixels.</param>
    /// <param name="getValue">Delegate that extracts the numerical value from a data item.</param>
    /// <returns>A 2D chart builder.</returns>
    public FusionChartColumn2DBuilder<T> Column2D<T>(
        IEnumerable<T> data, 
        int width, 
        int height,  
        Func<T, double> getValue)
    {
        return new FusionChartColumn2DBuilder<T>(mHtmlHelper, mChartsFolderBase, data, getValue, width, height);
    }

    /// <summary>
    /// Creates a builder for 2D pie chart.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="data"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="getValue"></param>
    /// <returns></returns>
    public FusionChartPie2DChartBuilder<T> Pie2D<T>(
        IEnumerable<T> data, 
        int width, 
        int height,  
        Func<T, double> getValue)
    {
        return new FusionChartPie2DChartBuilder<T>(mHtmlHelper, mChartsFolderBase, data, getValue, width, height);
    }
}

So far I’ve only implemented two chart builders (one for bar charts, and one for pie charts), but FusionCharts supports a slew of charts that could easily be integrated into this API.

FusionChartsHelper doesn’t do much aside from instantiate the actual chart builders.  These are the fluent APIs for building and configuring a chart.  Let’s look at each of them:

/// <summary>
/// A builder for a 2D column chart.
/// </summary>
/// <typeparam name="T"></typeparam>
public class FusionChartColumn2DBuilder<T> : FusionChartBuilder<T>
{
    /// <summary>
    /// The filename of the chart.
    /// </summary>
    private const string CHART_NAME = "FCF_Column2D.swf";

    /// <summary>
    /// The label for the X axis.
    /// </summary>
    private string mXAxisLabel;

    /// <summary>
    /// The label for the Y axis.
    /// </summary>
    private string mYAxisLabel;

    /// <summary>
    /// Initializes the builder.
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="baseUrl">The URL to the folder that contains the SWF files for Fusion Charts.</param>
    /// <param name="data"></param>
    /// <param name="valueExtractor"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    public FusionChartColumn2DBuilder(HtmlHelper helper, string baseUrl, IEnumerable<T> data, Func<T, double> valueExtractor, int width, int height) :
        base(helper, baseUrl + CHART_NAME, data, valueExtractor, width, height)
    {
    }

    /// <summary>
    /// Writes the X and Y axis labels.
    /// </summary>
    /// <param name="xml"></param>
    internal override void WriteGraphProperties(StringBuilder xml)
    {
        if (mXAxisLabel != null) xml.AppendFormat(" xAxisName='{0}'", mXAxisLabel);

        if (mYAxisLabel != null) xml.AppendFormat(" yAxisName='{0}'", mYAxisLabel);
    }

    /// <summary>
    /// Sets the label for the X Axis.
    /// </summary>
    /// <param name="xAxisLabel"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> XAxisLabel(string xAxisLabel)
    {
        mXAxisLabel = xAxisLabel;

        return this;
    }

    /// <summary>
    /// Sets the label for the Y Axis.
    /// </summary>
    /// <param name="yAxisLabel"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> YAxisLabel(string yAxisLabel)
    {
        mYAxisLabel = yAxisLabel;

        return this;
    }
}

/// <summary>
/// A chart builder for 2D pie charts.
/// </summary>
/// <typeparam name="T"></typeparam>
public class FusionChartPie2DChartBuilder<T> : FusionChartBuilder<T>
{
    /// <summary>
    /// The name of the Pie Chart SWF file.
    /// </summary>
    private const string CHART_NAME = "FCF_Pie2D.swf";

    /// <summary>
    /// Flag that controls whether or not labels are shown by pie slices.
    /// </summary>
    private bool mShowLables = true;

    /// <summary>
    /// Initializes the builder.
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="chartUrl"></param>
    /// <param name="data"></param>
    /// <param name="valueExtractor"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    public FusionChartPie2DChartBuilder(HtmlHelper helper, string chartUrl, IEnumerable<T> data, Func<T, double> valueExtractor, int width, int height) 
        : base(helper, chartUrl + CHART_NAME, data, valueExtractor, width, height)
    {
    }

    /// <summary>
    /// Writes chart-specific XML settings. 
    /// </summary>
    /// <param name="xml"></param>
    /// <remarks>
    /// Derived classes should override this method to add any chart-specific markup to the
    /// &lt;graph&gt; element.  When called, the '&lt;graph ' markup will have been rendered already.  
    /// </remarks>
    internal override void WriteGraphProperties(StringBuilder xml)
    {
        if (mShowLables) xml.Append(" shownames='1'");
    }

    /// <summary>
    /// Hides the labels from the pie chart.
    /// </summary>
    /// <returns></returns>
    public FusionChartPie2DChartBuilder<T> HideLabels()
    {
        mShowLables = false;

        return this;
    }
}

Each class is fairly short and exposes only the functionality that is specific to its chart type.  All the common functionality and the core of the chart rendering is handled by the base class, FusionChartBuilder<T>: 

/// <summary>
/// Builds a chart.
/// </summary>
public abstract class FusionChartBuilder<T>
{
    ...
    
    /// <summary>
    /// Creates the builder.
    /// </summary>
    /// <param name="data">The data items to build a chart from.</param>
    /// <param name="valueExtractor">Used to get the value from data items.</param>
    /// <param name="helper"></param>
    /// <param name="chartUrl">The URL to the chart.</param>
    /// <param name="width">Chart width.</param>
    /// <param name="height">Chart height.</param>
    public FusionChartBuilder(HtmlHelper helper, string chartUrl, IEnumerable<T> data, Func<T,double> valueExtractor, int width, int height)
    {
        ...
    }

    /// <summary>
    /// Gets the next available color.
    /// </summary>
    /// <returns></returns>
    private string GetNextColor()
    {
        ...
    }

    /// <summary>
    /// Writes chart-specific XML settings. 
    /// </summary>
    /// <param name="xml"></param>
    /// <remarks>
    /// Derived classes should override this method to add any chart-specific markup to the
    /// &lt;graph&gt; element.  When called, the '&lt;graph ' markup will have been rendered already.  
    /// </remarks>
    internal abstract void WriteGraphProperties(StringBuilder xml);

    /// <summary>
    /// Adds an action link to each item. 
    /// </summary>
    /// <param name="actionLink"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> Action(Func<T, string> actionLink)
    {
        ...
    }

    /// <summary>
    /// Sets the ID of the generated chart.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> Id(string id)
    {
        ...
    }

    /// <summary>
    /// Specify a callback that extracts a friendly label for each item.
    /// </summary>
    /// <param name="getLabel"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> Label(Func<T, string> getLabel)
    {
        ...
    }

    /// <summary>
    /// Enables debug mode.
    /// </summary>
    /// <returns></returns>
    public FusionChartBuilder<T> EnableDebugMode()
    {
        ...
    }

    /// <summary>
    /// Sets the number of decimal places to show.
    /// </summary>
    /// <param name="precision"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> DecimalPrecision(int precision)
    {
        ...
    }

    /// <summary>
    /// When enabled this will round numbers to millions or thousands and add the
    /// corresponding suffix (k for thousands and m for millions).
    /// </summary>
    /// <param name="enabled"></param>
    /// <returns></returns>
    /// <remarks>
    /// Setting this to true corresponds to setting FormatNumberScale='1' on the
    /// graph XML element in FusionCharts.
    /// </remarks>
    public FusionChartBuilder<T> UseDynamicSuffixes(bool enabled)
    {
        ...
    }

    /// <summary>
    /// Sets the value to prepend to all numeric values.
    /// </summary>
    /// <param name="prefix"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> NumberPrefix(string prefix)
    {
        ...
    }

    /// <summary>
    /// Sets the value to append to all numeric values.
    /// </summary>
    /// <param name="suffix"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> NumberSuffix(string suffix)
    {
        ...
    }

    /// <summary>
    /// Builds a string of text to show as a chart item's tooltip.
    /// </summary>
    /// <param name="hoverLabelBuilder"></param>
    /// <returns></returns>
    public FusionChartBuilder<T> Hover(Func<T, string> hoverLabelBuilder)
    {
        ...
    }

    /// <summary>
    /// Renders the chart.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        //The real work happens here!
        ...
    }

    /// <summary>
    /// Sets the chart caption.
    /// </summary>
    /// <param name="caption"></param>
    public FusionChartBuilder<T> Caption(string caption)
    {
        ...
    }

    /// <summary>
    /// Sets the chart's subcaption.
    /// </summary>
    /// <param name="subCaption"></param>
    public FusionChartBuilder<T> SubCaption(string subCaption)
    {
        ...
    }
}

I’ve omitted the field and property definitions as they’re mundane (and available in the full source below).  The fluent methods are fairly simple and just do the standard “set and return”:

/// <summary>
/// Sets the chart caption.
/// </summary>
/// <param name="caption"></param>
public FusionChartBuilder<T> Caption(string caption)
{
    mCaption = caption;

    return this;
}

The real work occurs in the ToString method.  This method renders the FusionCharts markup and XML configuration data based on the data items and the configuration settings made with the fluent methods.  It also does a quick find-and-replace to correct the z-ordering problem with Flash:

/// <summary>
/// Renders the chart.
/// </summary>
/// <returns></returns>
public override string ToString()
{
    StringBuilder xml = new StringBuilder();

    xml.Append("<graph");

    WriteGraphProperties(xml);

    if (mDecimalPrecision >= 0) xml.AppendFormat(" decimalPrecision='{0}'", mDecimalPrecision);

    if (mUseDynamicSuffixes) xml.AppendFormat(" formatNumberScale='1'");

    if (mPrefix != null) xml.AppendFormat(" numberPrefix='{0}'", mPrefix);

    if (mSuffix != null) xml.AppendFormat(" numberSuffix='{0}'", mSuffix);

    if (mCaption != null) xml.AppendFormat(" caption='{0}'", mCaption);

    if (mSubCaption != null) xml.AppendFormat(" subCaption='{0}'", mSubCaption);

    xml.AppendLine(">");

    foreach (T item in Data)
    {
        xml.AppendFormat("<set value='{0}' color='{1}'", mValueExtractor(item), GetNextColor());

        if (mLabeler != null)
        {
            xml.AppendFormat(" name='{0}'", mHelper.UrlEncode(mLabeler(item)));
        }

        if (mLinkBuilder != null)
        {
            xml.AppendFormat(" link='{0}'", mHelper.UrlEncode(mLinkBuilder(item)));
        }

        if (mHoverLabelBuilder != null)
        {
            xml.AppendFormat(" hoverText='{0}'", mHelper.UrlEncode(mHoverLabelBuilder(item)));
        }

        xml.AppendLine("/>");
    }

    xml.AppendLine("</graph>");

    string markup = InfoSoftGlobal.FusionCharts.RenderChartHTML(mChartUrl, "", xml.ToString(), mChartId, 
                                                                Width.ToString(), Height.ToString(), mDebugEnabled);

    //We have to add another param to make sure the flash object doesn't shine through jQuery UI.
    markup = markup.Replace("<param name=\"quality\" value=\"high\" />",
                            "<param name=\"quality\" value=\"high\" /><param value=\"opaque\" name=\"wmode\" />")
        .Replace("<embed", "<embed wmode=\"opaque\"");

    return markup;
}

Also note that it calls the abstract WriteGraphProperties method, which allows derived classes to inject their own config settings.

Again, this is a pretty bare-bones API right now.  I’ve implemented just the charts and config options that I needed for the project I’m working on, but it could easily be extended with new settings and chart types.  If you’re going to use this in your own project, be sure you put the SWF files for FusionCharts in ‘/Charts’, or edit the hard-coded path in the code (or replace it with a config setting or static property that can be set from Global.asax). 

You can download the code here, graciously donated by InRAD.  Note that I have not tested this code outside of InRAD’s projects, so let me know if I missed a dependency. 

Thoughts?  Suggestions? 

Tags:

Nov 10 2009

A fluent HtmlHelper extension for using FusionCharts in ASP.NET MVC, Part 1

Category: ASP.NET | MVCMatt @ 10:26

We’ve been working like mad to get a (very) functional prototype of our new system running at work, which is why my posts have been rather sparse lately.  We’re doing interesting things, just not much that I can talk about.  Today is different though, I actually have something useful to share!

My current task revolves around rendering charts.  I’ve previously used a variety of charting libraries, mostly recently Telerik and dotnetCharting, but we wanted to go a different route this time.  Neither was a good fit for ASP.NET MVC the last time I looked.  So, I thought I’d pick up one of the many handy JavaScript charting libraries and be done with it.  Unfortunately, that didn’t pan out.  Flot is simply not flexible enough for my needs.  jqPlot was about the same.  Yeah, you can extend either of them, but I wanted something I could customize with less work (basically I wanted more built-in options).  I also looked at the Google solution, but that’s the exact opposite of flexible since it renders as a non-interactive image.  After lots of wailing around, I gave up on the JavaScript route and concluded that none of them are really mature.

I briefly considered Silverlight before deciding that I should see what Flash had available.  Much to my surprise, I found a great little charting library that works well enough with .NET: FusionCharts Free!  Out of all the charting APIs I’ve used over the years, this has quickly become my favorite.  You just feed in an XML definition of what the graph should look like, and it renders it.  It offers a slew of options, too, making it a good fit for what I needed.

Sadly the samples all deal with ASP.NET WebForms and not with ASP.NET MVC.  The same approach works, but I wanted something cleaner.  I decided to create an HtmlHelper extension that would allow me to build a FusionChart with a fluent interface.  Despite my aversion to fluent interfaces, I do think there are times where they make sense, and I think control builders for MVC are a good fit.  My approach when I make a fluent interface is simple: anything that’s required is a parameter of the head method in the fluent chain.  All other optional settings are exposed as fluent methods.  An alternative is to accept some sort of “settings” object that exposes all the settings as properties, and while this does allow for the same terse inline configuration, it’s less readable to me than the fluent version. 

Anyway, let’s look at the usage of this API.  You would call it from your view like any other HtmlHelper:

Html.FusionCharts().Column2D(Model.Data, 415, 247, d => d.TotalSales)
    .Label(d => d.ShortName)
    .Hover(d => d.LongName)
    .XAxisLabel("Widgets")
    .YAxisLabel("Sales")
    .DecimalPrecision(2)
    .UseDynamicSuffixes(true)
    .NumberPrefix("$")
    .Action(d => Html.BuildUrlFromExpression<WidgetController>(c => c.ViewWidget(d=>d.Id)))

Html.FusionCharts() returns a helper class that exposes methods for creating the various chart types (for now, I’ve only implemented the 2D column chart, but plan to do others).  Column2D is the head of the fluent chain and returns a FusionChartBuilder<T>:

/// <summary>
/// Gets a fusion chart builder that will create a 2D bar chart.
/// </summary>
/// <typeparam name="T">The type of the data items.</typeparam>
/// <param name="data">The items to bind to the chart.</param>
/// <param name="width">Width in pixels.</param>
/// <param name="height">Height in pixels.</param>
/// <param name="getValue">Delegate that extracts the numerical value from a data item.</param>
/// <returns></returns>
public FusionChartBuilder<T> Column2D<T>(
    IEnumerable<T> data, 
    int width, 
    int height,  
    Func<T, double> getValue)
{
    return new FusionChartBuilder<T>(mHtmlHelper, mUrlHelper.Content("~/Charts/FCF_Column2D.swf"),
            data, getValue, width, height);
}

The builder enables me to configure options on my charts cleanly from within my view.  I’m not exposing all the FusionChart options yet (and probably won’t), but so far I can control the chart labels, the tooltip that’s shown when you hover over a bar, the format of the numbers, and I can even add a link to each bar, enabling users to “drill-down” into the data or navigate to a corresponding details page.

Time is short, so I’m not going to get any further into the code today, but I will in a future post.  For now, feel free to tell me how you love/hate fluent APIs or what a brilliant/dumb developer I am for using Flash from ASP.NET.

Tags:

Sep 8 2009

Fluent wrapper for ASP.NET MVC TagBuilder

Category: ASP.NET | MVCMatt @ 07:35

I’m not a huge fan of the fluent-API.  I think it’s a pattern that’s been overused and is now applied like mayonnaise: people are putting it on things where it just doesn’t belong.  That said, there are times when it’s useful.  Case in point is building up an HTML snippet programmatically for use in ASP.NET MVC.  Unfortunately, the handy TagBuilder class actually isn’t fluent (at all).  Most of its methods return void.  Fortunately it is quite easy to adapt the TagBuilder into a fluent version.  Behold, FluentTagBuilder:

/// <summary>
/// Wrapper for <see cref="TagBuilder"/> that makes
/// it a fluent API.
/// </summary>
public class FluentTagBuilder
{
    private TagBuilder mBuilder;

    public FluentTagBuilder(string tagName)
    {
        mBuilder = new TagBuilder(tagName);
    }

    public FluentTagBuilder MergeAttribute(string key, string value)
    {
        mBuilder.MergeAttribute(key, value);

        return this;
    }

    public FluentTagBuilder AddCssClass(string cssClass)
    {
        mBuilder.AddCssClass(cssClass);

        return this;
    }

    public string InnerHtml
    {
        get
        {
            return mBuilder.InnerHtml;
        }
        set
        {
            mBuilder.InnerHtml = value;
        }
    }

    public override string ToString()
    {
        return mBuilder.ToString();
    }
}

Note that I haven’t exposed all the underlying TagBuilder functionality, just the bits I needed today.  Implementing the rest yourself should be trivial though.  Here’s an example use in an HtmlHelper extension:

public FluentTagBuilder SuperSecretButton(string url, string altText)
{
    FluentTagBuilder link = new FluentTagBuilder("a")
        .MergeAttribute("href", url)
        .MergeAttribute("target", "_blank");

    FluentTagBuilder image = new FluentTagBuilder("img")
        .MergeAttribute("src", mHelper.ResolveUrl(SEEKRET_URL))
        .MergeAttribute("alt", altText);

    link.InnerHtml = image.ToString();

    return link;
}

And the use of the helper in the view:

Html.MyHelper().SuperSecretButton("http://wherever", "I am a button").AddCssClass("analyzeCostResearch")

Note how the view can add a class (or potentially any other attribute) without clutering up the method signatures for the helper. 

Tags:

Aug 11 2009

Standardizing JSON Responses in ASP.NET MVC

Category: ASP.NET | MVC | liteGridMatt @ 04:05

ASP.NET MVC provides very nice support for returning JSON data, but my chief complaint with it is that it’s too flexible.  You can basically cram anything you want in it and trust that it will make it to the client script, which has lead to a complete lack of coherence among the various controllers in our big MVC application.  Some actions return a simple string that contains either “success” or “error” depending on whether or not the operation succeeded.  Others return true or false.  This is going to cause maintenance problems down the road, so we are taking a stab at standardizing things.  Right now, the idea is to create a class derived from JsonResult that exposes a few standard properties while at the same time maintaining the flexibility of the original JsonResult.  The common properties are “status”, a boolean that is true or false depending on success or failure of the requested action, and “message”, an optional string that can be set with additional details in the case of failure.  The custom result maintains the ability to insert additional properties into the JSON result via an object (anonymous or otherwise) as well as through adding key/value pairs explicitly.  The implementation is quite simple (commments removed for readability):

public class StandardJsonResult : JsonResult
{
    public string Message { get; set; }

    public bool Status { get; set; }

    public Dictionary<string, object> Properties { get; private set; }

    public StandardJsonResult()
    {
        Properties = new Dictionary<string, object>();
        Data = Properties;
        Status = true;
    }

    public StandardJsonResult(object data)
        : this()
    {
        IDictionary<string, object> properties = data.ToDictionary();

        foreach (var keyValue in properties)
        {
            Properties.Add(keyValue.Key, keyValue.Value);
        }
    }

    public StandardJsonResult(Exception ex) : this()
    {
        Status = false;
        Message = ex.Message;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        //Copy in standard properties
        Properties.Add("status", Status);

        if (Message != null)
        {
            Properties.Add("message", Message);
        }

        base.ExecuteResult(context);
    }
}

There are several constructors provided.  The one that takes an object as a parameter allows you to send anonymous objects or view models across the wire just like you can with a regular JsonResult object.  The values are copied from the object using the method I previous described, but it could also be done using a RouteDataDictionary.  The values are added to the Properties dictionary, which is actually assigned to the JsonResult’s Data property.  The key/value pairs in the dictionary are serialized to properties in the JSON output.  The ExecuteResult method is overridden so that the two standard properties can be added to the dictionary prior to JSON serialization.

This class can also be extended for other recurring scenarios.  For example, here is the custom result that feeds data into liteGrid:

public class LiteGridJsonResult : StandardJsonResult
{
    public Array DataItems { get; private set; }

    public LiteGridJsonResult(Array dataItems)
    {
        DataItems = dataItems;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        Properties.Add("dataItems", DataItems);

        base.ExecuteResult(context);
    }
}

This custom result simply extends the standard one with a collection of data items that will be rendered by the client. 

Thoughts or suggestions?

Tags: