Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.
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 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:
namespaceFsWeb.ModelsopenMongoDB.BsonopenSystem.ComponentModel.DataAnnotations[<CLIMutable>]typeContact={_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:
namespaceFsWeb.ControllersopenSystem.LinqopenSystem.WebopenSystem.Web.MvcopenFsWeb.Models[<HandleError>]typeHomeController()=inheritController()let contacts = createLocalMongoServer() |> getMongoDatabase "contactDb" |> getMongoCollection "contacts"memberthis.Index()=contacts.FindAll().ToList()|>this.View[<HttpGet>]memberthis.Create()=this.View()[<HttpPost>]memberthis.Create(contact:Contact)=ifbase.ModelState.IsValidthencontact |> contacts.Insert |> ignorethis.RedirectToAction("Index"):>ActionResultelsethis.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>]ModuleMongoFs// Code removed for brevity
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:
namespaceFsWeb.ControllersopenSystem.LinqopenSystem.WebopenSystem.Web.MvcopenFsWeb.Modelsopen Raven.Client.Document[<HandleError>]typeHomeController()=inheritController()let ravenUrl = "http://localhost:8080/" let executeRavenAction action = use store = new DocumentStore() store.Url <- ravenUrl store.Initialize() |> ignore use session = store.OpenSession() action sessionmemberthis.Index()=executeRavenAction <| fun session -> session.Query<Contact>().ToList()|>this.View[<HttpGet>]memberthis.Create()=this.View()[<HttpPost>]memberthis.Create(contact:Contact)=ifbase.ModelState.IsValidthenexecuteRavenAction <| fun session -> session.Store contact session.SaveChanges()this.RedirectToAction("Index"):>ActionResultelsethis.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.
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.
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:
namespaceFsWeb.ControllersopenSystem.LinqopenSystem.WebopenSystem.Web.MvcopenFsWeb.Modelsopen FSharpCouch[<HandleError>]typeHomeController()=inheritController()let couchUrl = "http://localhost:5984" let dbName = "people"memberthis.Index()=getAllDocuments<Contact> couchUrl dbName |> Seq.map(fun c -> c.body)|>this.View[<HttpGet>]memberthis.Create()=this.View()[<HttpPost>]memberthis.Create(contact:Contact)=ifbase.ModelState.IsValidthencontact |> createDocument couchUrl dbName |> ignorethis.RedirectToAction("Index"):>ActionResultelsethis.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):
typeCouchDocument<'a>={id:stringrev:stringbody:'a}