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 andProductsController.fs
for working with products database). TheGlobal.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:
- 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 onNortwind.dbml
and select "Run Custom Tool" command. - You'll need to fix the reference from
WebApplication.Core
toWebApplication.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 thevsi
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#