Tomas Petricek

Searching for new ways of thinking in programming & working with data

I believe that the most interesting work is not the one solving hard problems, but the one changing how we think about the world. I follow this belief in my work on data science tools, functional programming and F# teaching, in my programming languages research and I try to understand it through philosophy of science.

The Gamma

I'm working on making data-driven storytelling easier, more open and reproducible at the Alan Turing Institute.

Consulting

I'm author of definitive F# books and open-source libraries. I offer my F# training and consulting services as part of fsharpWorks.

Academic

I published papers about theory of context-aware programming languages, type providers, but also philosophy of science.

Tomas Petricek
  • Tomas Petricek
  • Home
  • F# Trainings
  • Talks and books
  • The Gamma
  • Academic

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:

  • ASP.NET MVC - We're going to use ASP.NET MVC Framework to create the web application. As the article name suggests, most of the actual program code including models and controllers will be implemented in F#.
  • F# LINQ to SQL - The application uses a sample Northwind database and we'll write queries for selecting data from the database using LINQ support that's available in the F# PowerPack.
  • F# features - The application also uses some nice F# features that are quite useful for developing web applications. We'll use modules and records to implement the model and we'll also use advanced meta-programming features for constructing LINQ queries.

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!

  • WebApplication is the main MVC web application project. This is a C# project, but it doesn't contain almost any code, because model and all controllers are implemented in a referenced F# library. This project mainly contains views (aspx files) and other resources (such as stylesheets and database).
  • WebApplication.Core is an F# project that implements the core functionality of the application. In the sample application, it contains Model.fs, which implements the functionality for accessing data and two controllers (HomeController.fs for the main page and ProductsController.fs for working with products database). The Global.fs file registers routes for the URL rewriting.
  • WebApplication.Data is a simple C# project that contains only generated LINQ to SQL classes for our sample database. Although it is possible to rewrite the classes to F#, it is more convenient to use the tool available for C# in Visual Studio.

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:

  • Download Visual Studio 2010 project template (VSI, 808kB)
    NOTE: Save the file with the vsi extension and then open it to import the template.
  • Download the sample solution (ZIP, 924kB)

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

Contact & about

This site is hosted on GitHub and is generated using F# Formatting and DotLiquid. For more info, see the website source on GitHub.

Please submit issues & corrections on GitHub. Use pull requests for minor corrections only.

  • Twitter: @tomaspetricek
  • GitHub: @tpetricek
  • Email me: tomas@tomasp.net

Blog archives

October 2020 (1),  July 2020 (1),  April 2020 (2),  December 2019 (1),  February 2019 (1),  November 2018 (1),  October 2018 (1),  May 2018 (1),  September 2017 (1),  June 2017 (1),  April 2017 (1),  March 2017 (2),  January 2017 (1),  October 2016 (1),  September 2016 (2),  August 2016 (1),  July 2016 (1),  May 2016 (2),  April 2016 (1),  December 2015 (2),  November 2015 (1),  September 2015 (3),  July 2015 (1),  June 2015 (1),  May 2015 (2),  April 2015 (3),  March 2015 (2),  February 2015 (1),  January 2015 (2),  December 2014 (1),  May 2014 (3),  April 2014 (2),  March 2014 (1),  January 2014 (2),  December 2013 (1),  November 2013 (1),  October 2013 (1),  September 2013 (1),  August 2013 (2),  May 2013 (1),  April 2013 (1),  March 2013 (1),  February 2013 (1),  January 2013 (1),  December 2012 (2),  October 2012 (1),  August 2012 (3),  June 2012 (2),  April 2012 (1),  March 2012 (4),  February 2012 (5),  January 2012 (2),  November 2011 (5),  August 2011 (3),  July 2011 (2),  June 2011 (2),  May 2011 (2),  March 2011 (4),  December 2010 (1),  November 2010 (6),  October 2010 (6),  September 2010 (4),  July 2010 (3),  June 2010 (2),  May 2010 (1),  February 2010 (2),  January 2010 (3),  December 2009 (3),  July 2009 (1),  June 2009 (3),  May 2009 (2),  April 2009 (1),  March 2009 (2),  February 2009 (1),  December 2008 (1),  November 2008 (5),  October 2008 (1),  September 2008 (1),  June 2008 (1),  March 2008 (3),  February 2008 (1),  December 2007 (2),  November 2007 (6),  October 2007 (1),  September 2007 (1),  August 2007 (1),  July 2007 (2),  April 2007 (2),  March 2007 (2),  February 2007 (3),  January 2007 (2),  November 2006 (1),  October 2006 (3),  August 2006 (2),  July 2006 (1),  June 2006 (3),  May 2006 (2),  April 2006 (2),  December 2005 (1),  July 2005 (4),  June 2005 (5),  May 2005 (1),  April 2005 (3),  March 2005 (3),  January 2005 (1),  December 2004 (3),  November 2004 (2), 

License

Unless explicitly mentioned, all articles on this site are licensed under Creative Commons Attribution Share Alike. All source code samples are licensed under the MIT License.

CC License logo