Partager via


Overview: Building Data-Driven Websites

Applies to: Functional Programming

Authors: Tomas Petricek and Jon Skeet

Referenced Image

Get this book in Print, PDF, ePub and Kindle at manning.com. Use code “MSDN37b” to save 37%.

Summary: This overview discusses several technologies that can be used for programming web applications that access data. It also introduces areas where web applications can benefit from F#.

This topic contains the following sections.

  • Web and Data Technologies
  • Creating ASP.NET Websites
  • Using Databases in F# Projects
  • Summary
  • See Also

This article is associated with Real World Functional Programming: With Examples in F# and C# by Tomas Petricek with Jon Skeet from Manning Publications (ISBN 9781933988924, copyright Manning Publications 2009, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Web and Data Technologies

Most modern web applications work with data in one way or another. A typical web application keeps data created by users in a database. Mashups are another type of modern applications; they retrieve data from multiple external sources (such as web services) and combine them together.

The F# language can be used very effectively for working with data, so it is also quite suitable for developing data-processing parts of web applications. This article provides a brief review of standard .NET technologies for web and database programming. It demonstrates some interesting aspects of using them from F# and provides links to articles and tutorials with more information.

Creating ASP.NET Websites

The following list compares the two main frameworks for web development on .NET:

  • ASP.NET Web Forms is based on concepts such as pages and controls. To some extent, it hides many aspects of the underlying HTTP protocol, such as the stateless nature of the application. The handling of events in Web Forms application is based on postback, which means that the application makes requests to the same page, so it can react to events in the user interface.

  • ASP.NET MVC 3 is a modern web development framework based on the model-view-controller (MVC) design pattern. The programming model is based on the request-based stateless nature of the HTTP protocol. The application model contains business logic separated from any user interface aspects. Requests are handled by controllers that use the model and return a view that produces the application's user interface.

It is possible to use F# with both frameworks. However, ASP.NET MVC is a better fit thanks to its stateless nature and separation of concerns. The tutorials and examples in this chapter use F# to develop models and controllers for MVC web applications. The view component is created using a Razor view engine, which is based on C# syntax.

Using Online Templates for F#

The standard installation of Microsoft Visual Studio 2010 doesn't include a template for creating F# web applications using ASP.NET MVC. However, it is possible to use one of the many online templates contributed by the community. The articles in this chapter use the F# Empty Web (MVC3, Razor) template, which creates a simple solution consisting of an F# back-end and C# front-end. Figure 1 demonstrates selecting the template after its installation.

Figure 1. The newly installed template for F# web applications

Referenced Image

The following resources contain more information about the F# template and about the differences between ASP.NET MVC and ASP.NET Web Forms:

Asynchronous Programming

One of the main benefits of F# is its support for asynchronous programming via asynchronous workflows. Many server-side applications perform I/O requests such as working with files, transferring large amounts of data to or from databases, or calling web services. These tasks may take a long time. When executed synchronously, the thread that performs them is blocked waiting for the result but is not doing any useful work.

F# asynchronous workflows make it possible to write code that looks like synchronous but doesn't block threads when performing long-running tasks. This is the key for writing scalable server-side programs.

Many web applications, especially mashups, need to obtain data from various sources. To make this efficient, ASP.NET MVC 3 provides asynchronous controllers. This is a way to write scalable web applications that avoid unnecessary blocking of threads. In F#, this can be made even easier using a simple helper class named FSharpAsyncController. The following listing shows a brief example:

type MainController() =
    inherit FSharpAsyncController()

    // Asynchronous action that downloads a web page
    // from another web server and extracts its title
    member x.IndexAsync() = 
        async { let wc = new WebClient()
                let! html = wc.AsyncDownloadString(pageUrl)
                x.ViewData.Model <- extractTitle html }
        |> x.RunAsyncAction                                  

The listing implements a controller with a single action called Index. Using the default configuration, the action is invoked by the runtime when the user comes to the web application's home page. The IndexAsync member is called to start processing the request. It runs an asynchronous workflow that downloads the content of some external website (specified by pageUrl that is not defined in the above snippet) and extracts the page's title. The data is then passed to the view (not shown in the listing) using ViewData property.

The use of asynchronous workflows in the above example means that the web application can process a significantly larger number of web requests than if it were written in a synchronous fashion. The following tutorial presents a larger example of using asynchronous workflows for web programming. It implements a web application that aggregates news from various websites using RSS feeds:

Using Databases in F# Projects

As already mentioned, a large percentage of web applications store some data in a database. This section gives a brief overview of .NET technologies that can be used for working with databases, mainly focusing on Microsoft SQL Server.

The following list compares database technologies from the F# point of view:

  • ADO.NET has been available in .NET since its first version. It provides the most direct way for executing database queries and can be used to call SQL stored procedures. Thanks to F# constructs (such as the dynamic operator), ADO.NET can be used quite elegantly for many simple tasks.

  • LINQ to SQL enables writing database queries using the F# language instead of raw SQL. When using Language Integrated Query (LINQ) to SQL from F#, it is usually necessary to reference a domain model generated by SQLMetal in C#. However, queries can be written in F# using the LINQ support provided in the F# PowerPack. Queries can be written using sequence expressions and higher-order functions, so the syntax is more appropriate for simpler queries.

  • ADO.NET Entity Framework includes LINQ to Entities, which is a more evolved version of LINQ to SQL. It can be used in a similar way as LINQ to SQL, although the query language supported by LINQ to Entities is slightly more limited and lacks support for tuples and F# records. Entity Framework 4 also supports code-first development, which doesn't require the explicit creation of a domain model.

The next two sections briefly demonstrate how to use LINQ to SQL and how to use ADO.NET elegantly with the dynamic operator. For more information about individual technologies, see the following articles:

Using ADO.NET from F#

This section presents a brief example of using ADO.NET from F#. It uses an implementation of the dynamic operator (?) that makes it easy to call stored procedures and read the results. The implementation is discussed in one of the articles referenced at the end of the section.

When reading the data from a database, the result is typically stored in some data structure. In F#, such data structures are very easy to define using a record type:

type Product = { ID : int; Name : string; }

A record is a simple immutable data type that contains several (named) fields. Now, assume a database stores products and contains a stored procedure GetProducts. The procedure doesn't take any parameters and returns rows containing an ID and a Name column. The following listing builds a list of results:

let db = new DynamicDatabase(connectionString)
let products = 
  [ for row in db.Query?GetProducts() do
        yield { ID = row?ID; Name = row?Name } ]

The snippet uses helper type DynamicDatabase. The type contains a member named Query, which provides an implementation of the dynamic operator (?) that runs a specified stored procedure and returns the result set as a sequence. Next, the snippet iterates over the result in a sequence expression. The value row also implements the dynamic operator. This time, it can be used to access columns from the result. The dynamic operator is implemented such that it automatically converts the database value to a type inferred from the context—in this case, from the types of fields of the Product record.

The DynamicDatabase helper is discussed in detail in the following How-To article. The tutorial uses it to develop a simple web application showing a poll:

Using LINQ to SQL from F#

When using ADO.NET directly, a part of code needs to be written in SQL. The previous section assumed that the SQL database contained a stored procedure for selecting products. This procedure would have to be implemented in SQL (Tutorial: Creating an Online Poll gives a few examples). The aim of the LINQ approach is to allow writing queries in the host language such as C# or F#.

In F# 2.0, queries can be written using the sequence comprehension syntax and several higher-order functions provided by the Query module in F# PowerPack. The sequence comprehension is wrapped inside a quotation using the <@ … @> syntax. This allows the runtime to analyze the syntax and translate it to SQL.

The following listing shows a simple example that selects products from a database and finds the category where the product belongs (assuming that each product belongs to a single category):

let db = new Northwind(connectionString)
let products =
    <@ seq { for p in db.Products do  
                 for c in db.Categories do 
                     if p.CategoryID = c.CategoryID then 
                         yield (p.ProductName, c.CategoryName) } @>
    |> query

The snippet first creates an instance of the data model named Northwind. This is a class that is usually generated from a database. The tool for generating data models doesn't support F#, so the model is typically compiled into a separate assembly.

The rest of the snippet shows a simple query. The query is written as a sequence expression with two nested for loops (to iterate over all products and categories). The body of the loop matches products with corresponding categories and returns a tuple containing the product and category names. The query implements a database join. When executed as SQL, the code will be translated into a command with multiple FROM clauses.

The following resources provide more information about using LINQ to SQL in F#:

Summary

This article briefly reviewed .NET technologies for developing web applications and working with databases. It also provided links to tutorials and other articles that demonstrate how to use these technologies from F#. The examples in this article included working with a database using ADO.NET and LINQ to SQL and implementing an asynchronous controller for ASP.NET MVC 3.

A typical modern web application combines information from various sources. It uses database to keep its own data, calls web services or retrieves information using RSS feeds or other formats. The F# language is a very efficient tool for implementing data processing, so it can also be a good fit for the back-end of a web application. In addition, a scalable application should avoid blocking threads when performing long-running operations (such as I/O and web service communication). In F#, this can be done very easily thanks to asynchronous workflows.

See Also

This article is based on Real World Functional Programming: With Examples in F# and C#. Book chapters related to the content of this article are:

  • Book Chapter 7: “Designing data-centric programs” explains how to design applications that are primarily designed to work with or create a data structure. This is very often the design used by ASP.NET MVC models.

  • Book Chapter 12: “Sequence expressions and alternative workflows” contains detailed information on processing in-memory data (such as lists and seq<'T> values) using higher-order functions and F# sequence expressions.

  • Book Chapter 13: “Asynchronous and data-driven programming” explains how asynchronous workflows work and uses them to write an interactive script that downloads a large dataset from the Internet.

To download the code snippets shown in this article, go to https://code.msdn.microsoft.com/Chapter-5-Bulding-Data-ec639934

Previous article: How to: Call Native Libraries from F#

Next article: Tutorial: Creating a Web Project in F# Using an Online Template