Getting Started with MongoDB

When I first got started with MongoDB in my ASP.NET development I wasn't able to find as much information as I hoped to on how to get started with MongoDB. All of the core documentation focuses on interacting with the driver and doesn't give a high-level overview of how to use the driver in an actual project. After getting several projects up and running on MongoDB, I wanted to take a break and provide this much needed guide.

Installing MongoDB

There is no lack of information on how to get MongoDB up and running on a windows machine. The main MongoDB website has a great quickstart guide. This guide will enable you to get MongoDB installed and running locally and provides the necessary information to run MongoDB as a service. I prefer to start up MongoDB manually and avoid running it as a service on my development machine.

Starting Your Website Project

Now that you have MongoDB up and running on your machine, you can launch Visual Studio and create a new empty ASP.NET MVC3 project. Once Visual Studio has created and opened the new project, launch NuGet and install FluentMongo. FluentMongo will also install the mongo-csharp-driver.

We are installing FluentMongo because the core mongo-csharp-driver does not have LINQ support just yet. It will be coming in the next major version which should happen in a couple of months. Once this new version is out, this guide will still be relevant—I'll just update everything to make sure that everything works with the new LINQ support. FluentMongo isn't a perfect solution, but it does give you enough of a familiar environment that you can ease into MongoDB without introducing the query language running under the hood until you are ready.

Create an Initial Skeleton

We can put this project together quickly with just a single model, controller and view. We'll start with the model, Models\Record.cs. We're giving this object three fields: the primary key (_id), a text field, and a datetime field. Each document of a MongoDB collection is assigned an ObjectId in the _id field. This key will be randomly generated when a record is inserted into MongoDB.

using MongoDB.Bson;

public class Record
{
    public ObjectId _id { get; set; }
    public string SomeRandomText { get; set; }
    public DateTime RecordDate { get; set; }
}

Our controller initially just needs two actions: an index to list all of the records, and an action to create a new record based on text supplied by the user. We'll fill in the details of Controllers\HomeController.cs later.

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using FluentMongo.Linq;

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult CreateRecord(string SomeRandomText)
    {
        return RedirectToAction("Index");
    }
}

Finally, we'll add our view, Views\Shared\Index.cshtml. This view will include the form to create a new record. As the controller will create the record and redirect back to the index action, we don't need more than this initial view.

<h2>List of Records</h2>

<p>placeholder</p>

<h2>Insert New Record</h2>

@using (Html.BeginForm("CreateRecord", "Home"))
{
    <label>Some Random Text:</label>
    @Html.TextBox("SomeRandomText")
    <input type="submit" value="Create New Record" />
}

With that done, we can go ahead and start our web application:

Creating Data in MongoDB

Our interaction with MongoDB will start with populating the CreateRecord action. This method is pretty straightforward: we connect to mongo, create a new record, and save the record. I'm sure you will notice that I have not performed two actions that would have been absolutely necessary if we were using SQL Server: create the FluentMongoExample database and the Record table. MongoDB will create all databases and collections as they are are required by queries. Seeing this for the first time really drives home the full impact of using a schema-less database.

public ActionResult CreateRecord(string SomeRandomText)
    {
        MongoDatabase database = MongoDatabase.Create("mongodb://localhost/FluentMongoExample");
        MongoCollection collection = database.GetCollection("Record");

        Record record = new Record();

        record.SomeRandomText = SomeRandomText;
        record.RecordDate = DateTime.Now;

        collection.Save(record);

        return RedirectToAction("Index");
    }

Now that we are able to create new records, we can start listing all records. Querying our records with Fluent Mongo is virtually identical to how we would normally select records using LinqToEntities or LinqToSQL.

public ActionResult Index()
    {
        MongoDatabase database = MongoDatabase.Create("mongodb://localhost/FluentMongoExample");
        MongoCollection collection = database.GetCollection("Record");

        IQueryable records = from r in collection.AsQueryable() select r;

        return View(records);
    }

With the controller updated, we just need to update the Index view so that it uses the IQueryable<Record> model that our index controller is passing. I'm just using a simple for loop in a table for this example. Most of the WebGrid are supported by FluentMongo—but not all.

@model IQueryable<Record>

<h2>List of Records</h2>

<table cellspacing="3" cellpadding="3">
<tr>
    <th>_id</th>
    <th>SomeRandomText</th>
    <th>RecordDate</th>
</tr>
@foreach (Record record in Model)
{
    <tr>
        <td>@record._id</td>
        <td>@record.SomeRandomText</td>
        <td>@record.RecordDate</td>
    </tr>
}
</table>

With everything updated, we can rebuild or application and enter some records:

Deleting Data in MongoDB

Deleting data from MongoDB gets a touch more complicated. To illustrate th issue, we'll add the stubs to delete a record from our MongoDB collection.

In Controllers\HomeController.cs:

    public ActionResult DeleteRecord(ObjectId record_id)
    {
        return RedirectToAction("Index");
    }

In Views\Shares\Index.cshtml:

    <tr>
        <td>@record._id</td>
        <td>@record.SomeRandomText</td>
        <td>@record.RecordDate</td>
        <td>
            @Html.ActionLink("Delete", "DeleteRecord", new { record_id = record._id })
        </td>
    </tr>

If we click our new delete button, we get the following exception:

We get this exception because the ASP.NET MVC framework does not understand how to properly convert a BSON ObjectId to and from a string. We need to provide a model binder for the ObjectId.

In Models\Record.cs:

using System.Web.Mvc;

public class ObjectIdBinder : IModelBinder
{
    public object BindModel(ControllerContext controller_context, ModelBindingContext binding_context)
    {
        ValueProviderResult result = binding_context.ValueProvider.GetValue(binding_context.ModelName);
        return new ObjectId(result.AttemptedValue);
    }
}

In Global.asax.cs:

        protected void Application_Start()
        {
            ModelBinders.Binders.Add(typeof(ObjectId), new ObjectIdBinder());

            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }

With the binder added, we are able to successfully trigger our delete action and be redirected back to the index.

Documents are deleted from a mongo collection by providing a query that will match the documents to be deleted. This is not something that is provided by FluentMongo—we must use a native MongoDB query. Luckily, this is very easy to do. The native MongoDB queries provide a much more controlled and robust means of interacting with MongoDB, but we're interested in quick and simple for this tutorial.

    public ActionResult DeleteRecord(ObjectId record_id)
    {
        MongoDatabase database = MongoDatabase.Create("mongodb://localhost/FluentMongoExample");
        MongoCollection<Record> collection = database.GetCollection<Record>("Record");

        Record record = (from r in collection.AsQueryable() where r._id == record_id select r).FirstOrDefault();

        if (record != null)
        {
            collection.Remove(Query.EQ("_id", record._id));
        }

        return RedirectToAction("Index");
    }

With the delete action finished, we have a working data-driven application.

I have added my project to GitHub so that you can download and start tinkering yourself. Enjoy!