Partager via


Step 2: Displaying Aggregated News Items

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 tutorial shows how to use asynchronous F# functions to download news items from an asynchronous controller provided by ASP.NET MVC and how to render news using the Razor view engine.

This topic contains the following sections.

  • Generating Web Pages Asynchronously
  • Using Asynchronous Controllers
  • Formatting News Items Using a View
  • Summary
  • Additional Resources
  • 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.

Generating Web Pages Asynchronously

When serving a page to the client, the News Aggregator spends most of the time waiting for a response and data from the web sites that return RSS feeds. A realistic version of the web site would probably use some caching so that it doesn't have to wait every time, but most web servers need to wait for some I/O operation that should be implemented asynchronously.

When handling an HTTP request synchronously, ASP.NET runs all of the processing code on a dedicated thread. When the processing invokes a long-running asynchronous operation, it blocks the thread and limits the web application's scalability. However, ASP.NET MVC also makes it possible to handle requests asynchronously. In this case, it calls the controller, which can start an operation. The controller can notify the runtime when it completes so the runtime doesn't need to use a dedicated thread for the request. This is done using asynchronous controllers, which are introduced in the next section.

Using Asynchronous Controllers

A controller can contain multiple actions corresponding to the different pages it can generate. A standard synchronous action is a method that has the same name as the view (for example, Index). The method may have some parameters (such as values from a form or query string arguments) and eventually returns a ViewResult.

An asynchronous action is implemented by two methods. A method with the Async suffix (for example, IndexAsync) can have parameters and is called when the processing starts. The method can schedule a number of background tasks and notifies an AsyncManager about this number. When the operations complete, the background tasks can store results to the AsyncManager and notify it about their completion. The manager then calls the second method, named, for example, IndexCompleted. Finally, the method can pass the data to the view and return ViewResult.

In F#, it is possible to hide the programming model provided by ASP.NET MVC and just use an F# asynchronous workflow to implement the asynchronous action. The next section implements a wrapper that hides the complexity.

Using F# Asynchronous Workflows

When implementing an asynchronous controller using the standard mechanisms, the controller type needs to inherit from AsyncController instead of just Controller. An asynchronous controller implemented using F# asynchronous workflows will inherit from the FSharpAsyncController type, which is implemented in the next listing (it can be placed in a separate file in the FSharpWeb.Core project):

namespace FSharpWeb.Core
open System.Web.Mvc

/// Asynchronous controller that provides a method 
/// for running F# asynchronous workflows
type FSharpAsyncController() = 
    inherit AsyncController()

    /// Runs the specified asynchronous workflow as a controller
    /// action and reports completion to AsyncManager
    member x.RunAsyncAction(workflow) =
        x.AsyncManager.OutstandingOperations.Increment() |> ignore
        Async.Start(async { 
            try
                do! workflow
            finally
                x.AsyncManager.OutstandingOperations.Decrement() 
                |> ignore })       

The AsyncController type exposes an object that controls the execution of asynchronous actions via the AsyncManager property. The method RunAsyncAction makes it possible to run an asynchronous workflow without worrying about the AsyncManager object. When called, the method notifies the object that an operation has started. Then, it starts an asynchronous workflow that calls the workflow specified by the caller (using the do! syntax). When the specified workflow completes, the method notifies the manager that all background operations have ended. To guarantee that the number of outstanding operations will be decremented even if an error occurs, the snippet uses try … finally inside an asynchronous workflow.

The wrapper isn't very complicated but it makes writing asynchronous controllers in F# much easier. The following section shows how to use this helper to implement a controller for the News Aggregator web.

Implementing an Asynchronous News Controller

The previous step of the tutorial discussed the implementation of the model. The model is just a module with an asynchronous function AsyncDownloadAll that downloads and parses all of the RSS feeds. The implementation of the controller just needs to connect all of the pieces together to call the function and then return the result to the view. Using the helper class from the previous section, this can be written as follows:

namespace FSharpWeb.Core.Controllers
open System.Web.Mvc
open FSharpWeb.Core

[<HandleError>]
type MainController() =
    inherit FSharpAsyncController()

    member x.IndexAsync() = 
        async { 
            let! news = Model.AsyncDownloadAll()
            x.ViewData.Model <- news }
        |> x.RunAsyncAction                                  

    member x.IndexCompleted() =
        x.View()

The class that implements the controller inherits from the FSharpAsyncController (implemented in the previous section), so it can easily implement actions using F# asynchronous workflows. The News Aggregator has a single action called Index. The action is implemented using two methods. The IndexAsync method starts the processing. It creates an asynchronous workflow that downloads the news and stores the result in the ViewData.Model property, which will be returned to the view. The workflow has to be started using RunAsyncAction so that ASP.NET MVC is aware of the pending operation. When the workflow completes, the MVC framework calls the IndexCompleted method. In the above example, the method just returns the view (because the result is already stored in ViewData).

The last part of the application that needs to be implemented is the view. The following section shows how to do that.

Formatting News Items Using a View

The view receives a NewsPage value (declared in the previous step) that contains the news from all of the sources and is responsible for rendering it. In the News Aggregator application, the rendering is implemented using the Razor engine, which allows programmers to mix HTML markup with embedded C# code for looping (and other tasks).

By default, the view receives all of the data using the C# dynamic type and the code is checked dynamically. However, it is possible to specify the type of data using the @model keyword. This way, the compiler can check whether the code properties of NewsPage are used correctly. The rendering creates a <div> element for every feed containing an <ul> list with articles:

@{ View.Title = "Home Page"; }
@model FSharpWeb.Core.NewsPage

@foreach(var feed in Model.Feeds)
{
    <div class="feed">
    <h2>@feed.Item1</h2>
    <ul>
    @foreach (var item in feed.Item2)
    {
        <li>
            <h3><a href="@item.Link">@item.Title</a></h3>
            <p>@item.Description</p>
        </li>
    }
    </ul>
    </div>
}

The rendering is implemented using two nested foreach loops. The forach construct is written in C# but is prefixed by the @ symbol. The symbol denotes C# code embedded in the Razor markup. The outer loop iterates over all feeds (Model.Feeds). The values are tuples containing the feed title (which is printed as a heading) and a collection of all articles. The articles are printed in a nested foreach loop. The values stored in the nested collection have the FeedLink type, which contains article properties such as the title, link, and short description.

If you followed the tutorial from the beginning, you can now run the News Aggregator. The articles didn't discuss how to address the visual design of the application. You can find CSS styles that do that in the complete source code available below.

Summary

This tutorial demonstrated how to implement a mashup application that aggregates the news from several servers using RSS feeds. The downloading of the data as well as the processing of requests is implemented asynchronously, which is the key for writing scalable applications. In F#, this is largely simplified by asynchronous workflows.

The previous step of the tutorial implemented a model of the application, which contains the functions for downloading the data. This step added a controller and a view. The controller can be implemented asynchronously and the article demonstrated a helper that makes it easier to use F# asynchronous workflows for this task.

Additional Resources

This tutorial demonstrates how to create a web application that downloads RSS feeds from other web sites and aggregates them. The following two articles discuss the previous steps. The structure of the application was created using an online template and the first step implemented the downloading of the data:

More complex web applications usually also store their data in databases. More information about working with databases using ADO.NET as well as LINQ to SQL can be found in the following articles:

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

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.

The following MSDN documents are related to the topic of this article:

Previous article: Step 1: Downloading RSS Feeds

Next article: Tutorial: Creating an Online Poll