TP

ASP.NET and F# (I.) - Creating MVC web applications in F#

Some time ago, I wrote a couple of examples of developing web applications in F# using ASP.NET. Since then, the F# language and runtime has changed a little bit and there are also new technologies available in ASP.NET, so I thought I'd write a more up-to-date article on this topic. In this article, I'll present a simple "demo" F# web application that you can use as a starting point when creating your own projects (you'll also find a convenient Visual Studio 2010 template below). The article shows the following interesting things:

If you want to use F# for creating an MVC application, you have a few options. It should be possible to create the web application solely as an F# project. However, we'll use a more convenient approach. We'll create a standard C# MVC project and move all the actual implementation to an F# library. We'll look at the application structure shortly. The following screenshot shows a page that lists products in the Northwind database:

Application structure

The most convenient way to create an MVC web application, which is implemented in F# is to mix several C# and F# projects in a single solution. F# doesn't include web project templates and doesn't implement all Visual Studio tools that are needed for comfortable web development, so we'll create a C# projects that allow us to use LINQ to SQL and ASP.NET designers. All important pieces of code will be written in F# though!

When creating the projects by hand, you'll need to translate the generated controllers from C# to F# and you'll need to add all relevant ASP.NET references to the F# library project (there is an absurd number of them). However, you shouldn't face any difficulties or tricky problems. In any case, it is easier to start using the Visual Studio 2010 template that you can download below.

Now that we looked at the application structure, we'll look at a couple of interesting places in the application. This may give you an idea of some benefits that F# provides to web developers (although the sample is only very basic!)

Implementing model in F# with LINQ

As already mentioned, I decided to use the F# implementation of LINQ to SQL for data access in the application. There are only a few articles about this topic, so we'll discuss it in some more details. As already mentioned, LINQ to SQL cannot generate the classes natively in F#, so I created a separate C# project that contains just the generated classes.

The following listing shows the content of the Model.fs (without a single function that will be discussed shortly). As you can see, I created one record type for storing information about products that will be displayed in the page (we'll use it later). The rest of the model is implemented as an F# module, which means that it will appear as static class to .NET. Modules are quite useful for storing functionality that doesn't need to be encapsulated in a class, so this is a perfect fit:

namespace WebApplication.Core

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq
open Microsoft.FSharp.Linq.Query

// Namespace with LINQ to SQL generated classes
open WebApplication.Data

// Stores information about product (will be used later)
type ProductInfo = {
  ID : int
  Name : string
  Category : string
  Price : System.Decimal }

// Implementes the model for our MVC application
module Model = 
  // Returns the 'Product' entity with the given ID
  let ProductDetail (id) =
    let dx = new NorthwindDataContext()
    <@@ seq { for p in dx.Products do
               if (p.ProductID = id) then yield p }
               |> Seq.head @@> |> query

The ProductDetail function first creates a new instance of the generated data context and then runs the query. A query in F# is written using quotations. This means that the data-processing code is enclosed in <@@ ... @@>, which instructs the compiler to store the expression tree of the code (instead of compiling it). The quotation is then passed to the built-in query function that interprets it as a LINQ query.

Inside the query, you can use sequence expressions as well as some basic functions from the Seq module. In our application, we're using sequence expression that selects all products with the specified ID and then use the Seq.head function to get only first such product.

Composing LINQ queries

The second function implemented in the model will return a list of products. I made it a bit more sophisticated to demonstrate some interesting capabilities of the F# support for LINQ. In particular, the function takes a parameter that can specify how to sort the products. The parameter is a quotation that represents the expression tree of a key selector that specifies how products should be sorted. A key selector is a function that takes a product and returns some its property (or even calculated value). For example, to sort products using the length of the product name, the key selector would be <@@ fun p -> p.ProductName.Length @@>. Let's first look at the code and I'll explain how the key selector is used after that:

let ListProducts (keySelector:Expr<Product -> 'T>) =
  let dx = new NorthwindDataContext()
  <@@ seq { for p in dx.Products |> Seq.sortBy %keySelector do
              for c in dx.Categories do
                if (p.CategoryID.Value = c.CategoryID) then
                  yield { ID = p.ProductID
                          Name = p.ProductName 
                          Category = c.CategoryName 
                          Price = p.UnitPrice.Value } } @@> |> query

The keySelector parameter has a type Expr<Product -> 'T>, which means that it is an expression tree (or a quotation, in the F# terminology) of some function that takes a Product and returns some value - the function is generic, so the value can be anything. It is used in the quoted expression as a parameter of the Seq.sortBy function. We use %keySelector, which means that the quotation of the key selector will be "spliced" into the quotation we're writing. When we pass the quoted query to the query function later, it will see the actual expression in place of the variable spliced using the % operator. This allows us to compose queries from individual pieces, which is quite useful and cannot be directly done in C# - we can for example dynamically compose the WHERE clause using conditions specified by the user.

The sample query also uses a sequence expression with nested for loops to implement the JOIN functionality. Although this isn't needed, because the Product entity contains a reference to the associated Category, I wrote the join explicitly to demonstrate a more complex LINQ query in F#. Finally, when returning the result using the yield keyword, we construct a value of the record defined in the previous listing (which is a bit simpler than declaring and creating a class in C#).

Implementing controllers in F#

Now that we have the module implementing the model component of our MVC application, we'll also need to implement the controller. We'll look at the content of the ProductsController.fs file, which is more interesting as it uses the model and also calls the ListProducts function with several different key selectors as arguments.

The class contains two members that implement two actions of the controller. The member List loads a list of products from the database. It has a parameter which may be null and specifies the ordering of products (simply as an integer). The second member returns details about the specified product and is named Detail:

let (|NonNull|_|) (a:Nullable<_>) =
  if a.HasValue then Some(a.Value) else None

type ProductsController() =
  inherit Controller()
  
  member x.List(id:Nullable<int>) =
    x.ViewData.Model <- 
      match id with
      | NonNull(1) -> Model.ListProducts <@@ fun p -> p.UnitPrice.Value @@>
      | NonNull(2) -> Model.ListProducts <@@ fun p -> p.CategoryID.Value @@>
      | _ -> Model.ListProducts <@@ fun p -> p.ProductName @@>
    x.View() 

  member x.Detail(id:int) =
    x.ViewData.Model <- Model.ProductDetail id
    x.View() 

The listing first defines a simple active pattern that is useful when working with Nullable<T> values. It matches when the value given as an argument contains some value and fails when the value is empty. We use it in the List member where we need to select the ordering. When the parameter contains 1, we order products by price, parameter 2 specifies ordering by category and in all other cases (including empty value) we use default ordering by the product name. As you can see we specify several different key selectors (functions taking Product enclosed in quotation using <@@ ... @@>) of various return types. This is possible, because the ListProducts function is generic.

In both of the actions, we assign the result to the Model property of the ViewData. This allows the view to access the model in a type-safe way. The code in the view is standard C# code, so we won't look at it here - it is just worth mentioning that F# records appear as standard .NET classes to the C# code, so the integration is very smooth.

Summary

When developing ASP.NET MVC application in F#, the most convenient thing to do is to create a C# project and move all implementation to an F# library that is then referenced from the C# project (which contains only aspx files and other content related to the presentation layer). In F#, we can use LINQ for accessing data and the fact that quotations can be nicely composed using splicing allows us to implement some patterns that are not easy to write in C#. Moreover, F# features such as modules and records as well as pattern matching are quite useful in web development. However, this article contains only a very basic example that doesn't really take the full advantage of F#, so can only encourage you to download the template below and try experimenting yourself!

Visual Studio 2010 template

As already mentioned, I created a simple template based on the described project that allows you to simply create MVC web applications that use F# by selecting the template in Visual Studio 2010. To increase your appetite, the screenshot on the right is taken from my "New Project" dialog after installing the template. There are two minor gotchas with projects created from the template that you need to go through:

  1. You'll need to go to the WebApplication.Data project and generate the LINQ to SQL classes from the database description manually (for some reason, this doesn't happen automatically when you build the project). To do this, right click on Nortwind.dbml and select "Run Custom Tool" command.
  2. You'll need to fix the reference from WebApplication.Core to WebApplication.Data - simply remove the existing reference and add it again (choosing the reference to another project in the solution).

Once that's done, you should be able to run the project (assuming that you have SQL Server Express installed - the version that comes with Visual Studio 2010 is fine). In case you don't want a template for Visual Studio, you can also download the sample as a stand-alone solution:

Published: Sunday, 9 May 2010, 8:43 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: .net, functional, web, meta-programming, f#