F# Support for ASP.NET and Notes on Samples
More recent article available
This article is no longer up-to-date. Although the techniques described in the article might still work (with minor adjustments to the newer version of F#), it is now recommended to use a different approach. Also, the samples discussed in the article are not currently available. For more recent information see:As I mentioned earlier, I spent three months as an intern in Microsoft Research in Cambridge last year and I was working with Don Syme and James Margetson from the F# team. Most of the time I was working on the F# Web Toolkit, which I introduced on the blog some time ago [1], but I also worked on a few additions that are now part of the F# release. Probably the most useful addition is a new implementation of the CodeDOM provider for the F# language which makes it possible to use ASP.NET smoothly from F# (but it can be used in some other scenarios as well) together with two ASP.NET sample applications that you can explore and use as a basis for your web sites. This was actually a part of the distribution for a few months now (I of course wanted to write this article much earlier...), so you may have already noticed, but anyway, I'd still like to write down a short description of these ASP.NET samples and also a few tips for those who're interested in writing web applications in F#.
F# and ASP.NET
Let's start by looking at the ASP.NET examples. You can find them in the samples
directory
in your F# installation under the Web/ASP.NET
path. The directory also contains html
files with description of the projects and a guide to configuring them, but I'll describe both of these topics
in this post. The distribution contains two sample projects:
- AspNetIntro - this project is (almost) the simples possible F# web site, so it
can be used as a template for your web sites. It shows how to configure the CodeDOM provider, how to
write a simple page with code-behind and how to use the
App_Code
directory and data-binding. - PersonalWebSite - this is a more complex web site ported from the C# sample called Personal Web Site Starter Kit [2]. It demonstrates many of the standard ASP.NET 2.0 techniques including data access controls, master pages, membership and custom HTTP handlers.
ASP.NET Introduction using F#
To start playing with ASP.NET you'll need to open the project (I recommend copying it to your working directory first).
If you're using Visual Studio, you can select File - Open - Web Site...
in the menu and select the directory with
your project as demonstrated at Figure 1 below. The organization of ASP.NET projects is different than organization of ordinary
F# projects - in ASP.NET the project is just a directory and it contains all the files in the directory (this is also the reason
why you have to open it using a different command). The Figure 2 shows how the files of the ASP.NET Introduction project are
organized in the Solution Explorer:
As you can see, there are 6 files in the project. The Default.aspx.fs
and Default.aspx
together
form one web page and the DataBinding.aspx.fs
with DataBinding.aspx
form the second web page.
The App_Code
directory contains application logic that can be used from other pages in the project and in
our sample project it contains only one file (logic.fs
).
Finally, the web.config
file contains configuration of the whole application.
Before we look at the pages you may want to check the web.config
file, because it needs to contain the correct
reference to the CodeDOM provider implementation including a version of the current F# installation.
At the time of writing this article, the latest version is 1.9.3.14
, but if you're not sure what version
you are using, you can just start the fsi.exe
from the F# installation which prints the version number. The web.config
file
is an xml
file and it should contain the following content (with the right version number). The samples in the
distribution should contain the correct version number, but the incorrect configuration is a common issue when working
with ASP.NET in F#, so it is useful to know what the configuration should look like:
<?xml version="1.0"?>
<configuration><system.web>
<compilation debug="true">
<compilers>
<compiler
language="F#;f#;fs;fsharp" extension=".fs"
type="Microsoft.FSharp.Compiler.CodeDom.FSharpAspNetCodeProvider,
FSharp.Compiler.CodeDom, Version=1.9.3.14,
Culture=neutral, PublicKeyToken=a19089b1c74d0809"/>
</compilers>
</compilation></system.web>
</configuration>
Now, let's look at the Default.aspx
and Default.aspx.fs
files that together represent a simple page.
The page contains one button and one label (a control that can display some text) and when the user clicks on the button, the
result of some calculation is displayed in the label (the calculation is executed on the server-side). The following code is
a (slightly simplified) content of the Default.aspx
file, which defines the HTML markup together with the ASP.NET controls that are on the page:
<%@@ Page Language="F#"
CodeFile="Default.aspx.fs" Inherits="FSharpWeb.Default" %>
<html>
<body><form runat="server">
<asp:Button
ID="btnTest" RunAt="server" Text="Click me!"
OnClick="ButtonClicked" /><br />
<asp:Label ID="lblResult" RunAt="server" />
</form></body>
</html>
You can easily identify two server-side controls, because these are written using prefix asp
and
also contain the RunAt="server"
attribute, which means that the control is processed on the server-side.
You can use standard HTML notation for setting attributes of the controls. We set the ID
attribute
to both of the controls, which is important because it allows us to use them in the code-behind code. The OnClick
attribute of the button sets an event handler - a member of the FSharpWeb.Default
type from the code-behind file that
will be called on the server-side, when a user clicks on the button (which submits the HTML form and causes a page reload,
so the event can be processed on the server). The file also contains the first special line, which tells the ASP.NET engine that the
page is written in F# and it also tells what source file contains the code-behind code and what is the name of type declared in that file.
The code-behind file (Default.aspx.fs
) has the following content:
#light
namespace FSharpWeb
open System
open System.Web
open System.Web.UI.WebControls
type Default() =
inherit Page()
[<DefaultValue>]
val mutable btnTest : Button
[<DefaultValue>]
val mutable lblResult : Label
member this.ButtonClicked(sender, e) =
this.lblResult.Text <-
(sprintf "Factorial of 5 is: %d"
(FSharpWeb.Logic.factorial 5))
As we can see, the file contains an F# object type (named Default
), inherited from the base ASP.NET Page
type. Thanks to the recent improvements in F# it is possible to write the type using the new implicit class syntax (note the parentheses
after the type name), which means that you can place additional initialization code to the type declaration directly (following
the inherit
clause and F# will treat this as a constructor.
The class contains a mutable field for every control declared in the declarative markup with the name same as the ID
attribute in the markup file (btnTest
and lblResult
). The ASP.NET initializes these controls automatically
when it creates the page (that's why the fields are marked using mutable
), so we don't need to initialize these fields
in our code - F# doesn't usually allow uninitialized fields, but if you place the DefaultValue
before the field declaration,
it will be initialized to the default value (null
), which is correct, because the field will be later initialized by the
ASP.NET runtime. Finally, the type contains a member called ButtonClicked
, which is a
same name we used in the markup for the OnClick
attribute of the button control. This is an
event handler that will be called when user clicks on the button, it performs some calculation (the factorial
function is declared in the logic.fs
file) and sets a property of the other control that we have on the page.
Personal Web Site in F#
The second sample ASP.NET project is a port of quite a complex ASP.NET web site which also uses MS SQL database. The database
can be created manually as described in Welcome.html
in the PersonalWebSite
directory, but I also
uploaded the database to my web, so you can just download it and attach it in the SQL server (this is described below and the files for download
can be found at the end of the article).
The PWS_AspNetDb
is a database managed by ASP.NET that contains user information - the database below already contains
the required role (called Administrators
) and one user (admin
with password admin123!
).
The second database file (PWS_WebPersonal
) contains the application data, mainly information about photos and galleries
(as you can see from the screenshot in Figure 3).
The application uses many advanced ASP.NET features including the following:
- Master Pages - the overall structure of the web site is stored in a master page
(
Default.master
) and the other pages use theasp:Content
control to fill the place holders in the master page. - Site Maps - the map of the web site is defined in the
web.sitemap
file and is used for generating the menu in theDefault.master
file. - Themes - the design of the page including CSS files and images are managed using
ASP.NET Themes - there are two standard themes in the
App_Themes
directory. - Membership - the web uses standard ASP.NET technology for managing user accounts
including the controls for manipulating with users (e.g.
asp:CreateUserWizard
control is used inRegister.aspx
file). - Data Controls - ASP.NET data-access controls are used for reading the data from the application logic layer (implemented as a module in F#)
Configuring the databases
To configure the databases required by the demo, you can either follow the steps described in the Welcome.html file (which is part of the sample project), or if you already have some version of SQL Server installed on your machine, you can download the sample databases below (a bunch of MDF and LDF files) and attach them to the SQL Server. I'll explain how to do this using full version of SQL Server, but the steps needed with Express edition are very similar.
Once you downloaded the files, you'll need to launch SQL Server Management Studio and connect to the server instance. After doing
that you should see an "Object Explorer" window, where you can right click on the "Databases" group and select "Attach..." from the pop-up menu.
In the opened dialog window, you can add the databases (by selecting the MDF file) and click the "OK" button to attach the databases.
Finally, you'll need to modify the web.config
file in the Personal WebSite sample to include the following section:
<connectionStrings>
<add name="Personal" providerName="System.Data.SqlClient" connectionString=
"Data Source=.;Integrated Security=True;Database=PWS_WebPersonal" />
<remove name="LocalSqlServer"/>
<add name="LocalSqlServer" connectionString=
"Data Source=.;Integrated Security=True;Database=PWS_AspNetDb" />
</connectionStrings>
The Data Source
parameter in the connection string specifies the instance of the SQL Server, where the databases are attached.
By default this is the name of your computer (which can be written shortly using dot "."). When using the SQL Express the default
instance name is ".\SQLExpress". The Database
parameter specifies the name of the database, which you can see in the
"Object Explorer" in the SQL Server Management Studio. After following these steps, you should be able to run the demo application.
Data-access in F#
Most of the ASP.NET technologies used in the web site are used in almost the same way as in C#, so I will not
discuss them in larger details, but the last item in the list - the use of ASP.NET data controls together
with F# is I believe quite interesting, so I'll write a few notes about it. Let's look for example at the
Photos.aspx
file, which displays a list of photos in a gallery. The most important parts
of the file related to data access are shown in the following code snippet:
<asp:DataList RunAt="server" DataSourceID="photoSource" EnableViewState="false">
<ItemTemplate>
<!-- ... -->
<a href='Details.aspx?AlbumID=<%# base.Eval("PhotoAlbumID") %>'>
<img src="Handler.ashx?PhotoID=<%# base.Eval("PhotoID") %>&Size=S"
class="photo_198" style="border:4px solid white"
alt='Thumbnail of Photo Number <%# base.Eval("PhotoID") %>' />
</a>
<!-- ... -->
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="photoSource" RunAt="server"
TypeName="PersonalWebSite.PhotoManager"
SelectMethod="GetPhotos">
<SelectParameters>
<asp:QueryStringParameter Name="AlbumID" Type="Int32"
QueryStringField="albumID" DefaultValue="0"/>
</SelectParameters>
</asp:ObjectDataSource>
The first ASP.NET control used in the code is asp:DataList
, which is a control that
simply displays a collection of some items and uses a specified ItemTemplate
for displaying
every single item. In our case we're working with collection of records and the record has several members
(including PhotoID
and PhotoAlbumID
). To display a value of a record member
in the ASP.NET page we have to use ASP.NET Eval
method as you can see in the sample.
The Eval
method reads the specified member of the item in collection and writing the code
in <%# ... %>
tells ASP.NET that we want to render the result of the expression as
a part of the item template.
The record that we're using in this example is very simple and has the following structure:
type Photo =
{ PhotoID:int;
PhotoAlbumID:int;
PhotoCaption:string; }
This is actually one of the places where the F# version is shorter, because in C# version you have to write this as a class with properties, which makes the code quite longer.
The second server-side control (asp:ObjectDataSource
) in the first code snippet isn't visual, which means that it will
not produce any HTML output. It serves just as a declarative data-source for the data list and the properties
of this control specify how the content for the data list should be loaded. There are various other data-source
controls in ASP.NET (e.g. asp:SqlDataSource
which loads data directly from the database),
but the one that we're using in the example uses a specified .NET/F# type and calls a method of the given object
when it needs to retrieve the data. In F# this is even easier, because we can use a name of the module and the
code that retrieves the data can be a function in the module.
As you can see the property TypeName
is set to PersonalWebSite.PhotoManager
, which
can be treated as a module called PhotoManager
in a namespace PersonalWebSite
and
the SelectMethod
property, which specifies a name of the function that will be called when
loading the data is set to GetPhotos
. Finally, the SelectParameters
specifies
that the function expects one argument called AlbumID
and that the value of this argument
should be retrieved from the query string (that is from the URL of form photo.aspx?albumIdD=42
).
Now it is pretty easy to implement the module that matches this specifications and can be used as a data source
(we could of course use FLinq for loading the data, but I wanted to keep things simple for now, so we're using
standard SqlDataReader
object from .NET):
namespace PersonalWebSite
// ...
module PhotoManager =
// Function will be called by ASP.NET when loading data
// for the asp:DataList control in the 'Photos.aspx' page
let GetPhotos (albumID:int) =
// Open connection & create command
use conn = new SqlConnection(" ... ")
use cmd = new SqlCommand("GetPhotos", conn)
// Can the user see 'private' photos?
let usr = HttpContext.Current.User
let filter = not (usr.IsInRole("Friends") || usr.IsInRole("Administrators"))
cmd.CommandType <- CommandType.StoredProcedure)
cmd.Parameters.Add(SqlParameter("@@AlbumID", albumID)) |> ignore
command.Parameters.Add(SqlParameter("@@IsPublic", filter)) |> ignore
conn.Open()
// Read all photos into a .NET ResizeArray type
let list = new ResizeArray<_>()
use reader = command.ExecuteReader()
while (reader.Read()) do
list.Add({ PhotoID = unbox (reader.get_Item("PhotoID"))
PhotoAlbumID = unbox (reader.get_Item("AlbumID"))
PhotoCaption = unbox (reader.get_Item("Caption")) })
// Return the created collection
list
I hope this article explained some of the interesting things that you may find in the ASP.NET samples provided with the F# distribution and I think that the Personal WebSite Sample shows that F# can be used for writing quite complicated web applications as well. Of course, my initial motivation for writing web applications in F# is the possibility of using meta-programming to develop client-side code (the code that runs on the client as a JavaScript) in F# too, which makes development of modern "Ajax"-style applications easier, so if you're interested in this project you may want to look at F# Web Tools [1].
Downloads & References
- Download PWS Database Files (9.65 MB)
- [1] F# Web Toolkit: "Ajax" applications made simple [^] - Blog | TomasP.Net
- [2] Visual Web Developer Express: Samples and Starter Kits [^] - Microsoft.Com
Published: Saturday, 8 March 2008, 11:07 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: web, f#