Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint

Combining F# and NoSQL

NoSQL data storage options have been gaining in popularity over the past few years. This class of data store breaks away from the traditional RDBMS databases that have ruled for so long. NoSQL databases generally store data in unstructured or semi-structured formats rather than with the strict schemas used in relational databases. This approach makes it easier to scale horizontally, support redundancy, and handle massive amounts of data while making insertion and retrieval of that data amazingly fast. If you want to achieve faster and more scalable F# solutions that work well for both mobile and web applications, you’ll definitely want to learn about NoSQL databases and how to interact with them through F#.

In this section, I’ll show you how to use F# to interact with a few document-oriented NoSQL options. I’ll use a simple example of a basic grid that lists a minimal amount of information (first name, last name, and phone number) for a contact list. Additionally, a simple form for creating new contacts is provided. Since the primary focus is to show methods for getting started with inserting and retrieving records from these databases, I won’t spend much time on the UI aspects. You can see the full examples here.

MongoDB

MongoDB is one of the most popular NoSQL options in the .NET space. Like the other NoSQL databases discussed in this section, MongoDB is a document-oriented database. This means information pushed in the database is stored with a key and an associated document. When a document is later requested by the key, retrieval is exceptionally fast. Instructions on setting up MongoDB on Windows are available here.

Once you follow the steps for getting MongoDB up and running, the next step is to create a client to interact with that instance. The easiest way to get up and running with MongoDB and F# is to install the NuGet package with ID MongoFs into a project. This will pull the official C# MongoDB client as well as a simple F# wrapper that helps make interaction with the C# MongoDB client API from F# a little nicer.

As I mentioned previously, I will be showing a simple example for each of the three document-oriented data stores discussed in this section. Each example is an ASP.NET MVC 4 project that was set up with the C#/F# ASP.NET MVC 4 template that we discussed in Chapter 1. The example uses a record called Contact, as shown here:

namespace FsWeb.Models

open MongoDB.Bson
open System.ComponentModel.DataAnnotations

[<CLIMutable>]
type Contact = {
    _id : ObjectId
    [<Required>] FirstName : string
    [<Required>] LastName : string
    [<Required>] Phone : string
}

There are two aspects of this code that probably need a little explanation. The first is the ObjectId type. ObjectId is a 12-byte binary type provided by MongoDB. This type is commonly used as the unique ID for documents.

The second aspect is the CLIMutable attribute, which I briefly mentioned in all of the chapters up to this point. This attribute is a lesser-known feature of F# 3.0 that has tremendous value when working with records that need to be serialized or deserialized, such as when going to or from a document data store. When the F# compiler sees the attribute it automatically adds a parameterless constructor as well as getters and setters for the properties. This one little attribute opens the door for records to be used in places that were previously more difficult, such as in the controllers of an ASP.NET MVC application.

The code needed to retrieve all the stored contacts from MongoDB, as well as create new contacts, is shown in the following example:

namespace FsWeb.Controllers

open System.Linq
open System.Web
open System.Web.Mvc
open FsWeb.Models 

[<HandleError>]
type HomeController() =
    inherit Controller()

    let contacts = 
        createLocalMongoServer()
        |> getMongoDatabase "contactDb"
        |> getMongoCollection "contacts"

    member this.Index () =
        contacts.FindAll().ToList() |> this.View

    [<HttpGet>] 
    member this.Create () =
        this.View() 
    
    [<HttpPost>]      
    member this.Create (contact:Contact) =     
        if base.ModelState.IsValid then   
            contact |> contacts.Insert |> ignore
            this.RedirectToAction("Index") :> ActionResult
        else
            this.View() :> ActionResult

The emphasized code shows how little is needed to interact with MongoDB from F# when using MongoFs. The first four emphasized lines identify the MongoDB instance, database, and collection with which you wish to interact. Additionally, if the database or collection doesn’t already exist, it will be created. The pipelining approach used to accomplish these tasks is provided by the MongoFs library. The one line required for retrieving all records as well as the one line needed to insert a contact are achieved solely by the great syntax of F# combined with the API of the official C# MongoDB client.

One other thing you may have noticed is that I never open the MongoFs module in this example. This is due to another feature of F#, called the AutoOpen attribute. When the AutoOpen attribute is added to a module, that module will not need to be explicitly opened or referenced. Here’s an example of the AutoOpen attribute in use:

[<AutoOpen>]
Module MongoFs

// Code removed for brevity

RavenDB

RavenDB is another document-oriented database that has been taking the world by storm. A few of the big benefits of RavenDB include support for transactions, full-text search via Lucene, a “safe by default” core principle, and ease of embedding the DB when desired.

For the example that follows, download and extract the latest build of RavenDB. You can then launch the RavenDB server. Once that is complete, you should be able to run the example. The example was built by installing version 1.0.960 of the RavenDB.Client.FSharp NuGet package into an F#/C# ASP.NET MVC solution. The code needed to get our contacts list up and running is shown in the following example:

namespace FsWeb.Controllers

open System.Linq
open System.Web
open System.Web.Mvc
open FsWeb.Models
open Raven.Client.Document

[<HandleError>]
type HomeController() =
    inherit Controller()

    let ravenUrl = "http://localhost:8080/"

    let executeRavenAction action = 
        use store = new DocumentStore()
        store.Url <- ravenUrl
        store.Initialize() |> ignore
        use session = store.OpenSession()
        action session        

    member this.Index () =
        executeRavenAction
        <| fun session -> session.Query<Contact>().ToList()
        |> this.View

    [<HttpGet>] 
    member this.Create () =
        this.View() 
    
    [<HttpPost>]      
    member this.Create (contact:Contact) =     
        if base.ModelState.IsValid then   
            executeRavenAction
            <| fun session -> 
                 session.Store contact
                 session.SaveChanges()
            this.RedirectToAction("Index") :> ActionResult
        else
            this.View() :> ActionResult

While the code is a bit more verbose than the MongoDB example, you get a few key benefits such as inherent usage of the Unit of Work pattern. In addition to showing the RavenDB F# client API in use, the preceding code also showcases a higher-order function named executeRavenAction that abstracts away the common code that is used for the majority of interactions with RavenDB.

CouchDB

The last NoSQL option that I will showcase is CouchDB. Built with Erlang, CouchDB is one of the most fault-tolerant NoSQL options available. It’s also easy to distribute and fast. You can find out more about CouchDB here.

Note

The creator of CouchDB is now primarily focusing on a project called Couchbase, which has a different API and a number of additional features. I have chosen to only show CouchDB in this book; however, I recommend checking out Couchbase to see if it is right for you. A comparison of CouchDB and Couchbase is available here.

There are a number of client options for interacting with CouchDB. One of these options is one that I created, called FSharpCouch. To use it, install the FSharpCouch NuGet package in the desired project and use any of the functions described here. Here’s a quick example using the same contacts list application that was shown in the examples for the other two NoSQL options:

namespace FsWeb.Controllers

open System.Linq
open System.Web
open System.Web.Mvc
open FsWeb.Models
open FSharpCouch

[<HandleError>]
type HomeController() =
    inherit Controller()

    let couchUrl = "http://localhost:5984"
    let dbName = "people"

    member this.Index () =
        getAllDocuments<Contact> couchUrl dbName
        |> Seq.map(fun c -> c.body)
        |> this.View
        
    [<HttpGet>] 
    member this.Create () =
        this.View() 
    
    [<HttpPost>]      
    member this.Create (contact:Contact) =     
        if base.ModelState.IsValid then               
            contact |> createDocument couchUrl dbName |> ignore
            this.RedirectToAction("Index") :> ActionResult
        else
            this.View() :> ActionResult

One interesting aspect of this implementation is that F# records are used without any mutation required. The Contact record used for the create request only includes the things directly related, such as first name, last name, and phone. However, the entity or entities that are returned from a create or from either of the get options will return a record that includes an ID and a revision. This is made possible by returning the record shown in the following, with body set to a populated instance of a record that matches what was originally added (this is the main reason for the Seq.map call in the Index method of the previous example):

type CouchDocument<'a> = {
    id : string
    rev : string
    body : 'a
}
  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint