Using MongoDB with ASP.NET Web API

MongoDB is a popular NoSQL database that makes it a great backend for Web APIs which lend themselves towards a document store rather than a relational store. In this blog we show how you can use MongoDB with ASP.NET Web API to build an ApiController with support for HTTP GET, PUT, POST, and DELETE. Our sample controller is a simple contact manager Web API which builds on many of the same concepts as in the tutorial “Creating a Web API that Supports CRUD Operations” so I would recommend that you skim that for background.

In particular, the URI patterns are very similar to that of the tutorial:

Action HTTP Method Relative URI
Get a list of all contacts GET /api/contacts
Get a filtered list of all contacts GET /api/contacts?$top=2
Get a contacts by ID GET /api/contacts/id
Create a new contact POST /api/contacts
Update a contact PUT /api/contacts/id
Delete a contact DELETE /api/contacts/id

 

However, there are two differences worth noting:

  1. We host the sample ApiController in selfhost with a base URI of https://localhost:8080
  2. We have added support for OData-style queries on GET requests so that you can filter the set of contacts returned using $top, $skip, etc.

But I am getting ahead of myself – let’s get back to building the controller…

Note: Please see List of ASP.NET Web API and HttpClient Samples for the complete sample solution. Note that you still have to set up the MongoDB as described in the next section.

Prerequisites

Before you start you obviously need to download and install MongoDB. I followed the quick start instructions which has all the information you need and got me going in 10 minutes or so.

Second you need the MongoDB driver for C#. Here we use the MongoDB C# driver provided by 10gen and which is available as a NuGet package.

As an optional third step once your MongoDB installation has been set up you can use a tool like MongoVUE which allows you to browse and interact directly with the data in a MongoDB database.

Defining the Contact Type

We use the same basic Contact type used in “Creating a Web API that Supports CRUD Operations” but decorate it with an BsonIdAttribute (see the Mongo C# driver reference documentation for details) indicating which field is the id:

    1: public class Contact
    2: {
    3:     [BsonId]
    4:     public string Id { get; set; }
    5:  
    6:     public string Name { get; set; }
    7:  
    8:     public string Phone { get; set; }
    9:  
   10:     public string Email { get; set; }
   11:  
   12:     public DateTime LastModified { get; set; }
   13: }

Using MongoVUE we can see how Contact instances show up in the MongoDB database. The sample Contact data we use in this sample looks something like this

MongoVUE

Defining the Contact Repository

We use the common repository pattern to interact with the backend data source so the next step is to define the shape of the repository. We do this by defining an IContactRepository interface which we then implement so that it targets MongoDB. First, the interface is defined like this:

    1: public interface IContactRepository
    2: {
    3:     IEnumerable<Contact> GetAllContacts();
    4:  
    5:     Contact GetContact(string id);
    6:  
    7:     Contact AddContact(Contact item);
    8:  
    9:     bool RemoveContact(string id);
   10:  
   11:     bool UpdateContact(string id, Contact item);
   12: }

Implementing the Repository

It’s when implementing IContactRepository that we use the MongoDB C# driver API to connect to the data and to do the various CRUD operations defined by IContactRepository. First we set up the connection to database and get a Contact collection. By default we use the connection string “mongodb://localhost:27017” pointing at the localhost database we set up above.

For the purpose of this sample we also reset the database and add some default entries so we have something to start with. Note that the classes MongoServer, MongoDatabase, and MongoCollection<T> are all thread-safe so they can be used simultaneously by separate threads.

    1: public ContactRepository(string connection)
    2: {
    3:     if (string.IsNullOrWhiteSpace(connection))
    4:     {
    5:         connection = "mongodb://localhost:27017";
    6:     }
    7:  
    8:     _server = MongoServer.Create(connection);
    9:     _database = _server.GetDatabase("Contacts", SafeMode.True);
   10:     _contacts = _database.GetCollection<Contact>("contacts");
   11:  
   12:     // Reset database and add some default entries
   13:     _contacts.RemoveAll();
   14:     for (int index = 1; index < 5; index++)
   15:     {
   16:         Contact contact1 = new Contact
   17:         {
   18:             Email = string.Format("test{0}@example.com", index),
   19:             Name = string.Format("test{0}", index),
   20:             Phone = string.Format("{0}{0}{0} {0}{0}{0} {0}{0}{0}{0}", index)
   21:         };
   22:         AddContact(contact1);
   23:     }
   24: }

We now add the actual CRUD implementations as follows:

    1: public IEnumerable<Contact> GetAllContacts()
    2: {
    3:     return _contacts.FindAll();
    4: }
    5:  
    6: public Contact GetContact(string id)
    7: {
    8:     IMongoQuery query = Query.EQ("_id", id);
    9:     return _contacts.Find(query).FirstOrDefault();
   10: }
   11:  
   12: public Contact AddContact(Contact item)
   13: {
   14:     item.Id = ObjectId.GenerateNewId().ToString();
   15:     item.LastModified = DateTime.UtcNow;
   16:     _contacts.Insert(item);
   17:     return item;
   18: }
   19:  
   20: public bool RemoveContact(string id)
   21: {
   22:     IMongoQuery query = Query.EQ("_id", id);
   23:     SafeModeResult result = _contacts.Remove(query);
   24:     return result.DocumentsAffected == 1;
   25: }
   26:  
   27: public bool UpdateContact(string id, Contact item)
   28: {
   29:     IMongoQuery query = Query.EQ("_id", id);
   30:     item.LastModified = DateTime.UtcNow;
   31:     IMongoUpdate update = Update
   32:         .Set("Email", item.Email)
   33:         .Set("LastModified", DateTime.UtcNow)
   34:         .Set("Name", item.Name)
   35:         .Set("Phone", item.Phone);
   36:     SafeModeResult result = _contacts.Update(query, update);
   37:     return result.UpdatedExisting;
   38: }

Creating the Contact Controller

That’s it for the repository so we can now implement the actual ApiController. We base the implementation on the common pattern for supporting GET, PUT, POST, and DELETE as follows:

    1: public class ContactsController : ApiController
    2: {
    3:     private static readonly IContactRepository _contacts = new ContactRepository();
    4:  
    5:     public IQueryable<Contact> Get()
    6:     {
    7:         return _contacts.GetAllContacts().AsQueryable();
    8:     }
    9:  
   10:     public Contact Get(string id)
   11:     {
   12:         Contact contact = _contacts.GetContact(id);
   13:         if (contact == null)
   14:         {
   15:             throw new HttpResponseException(HttpStatusCode.NotFound);
   16:         }
   17:  
   18:         return contact;
   19:     }
   20:  
   21:     public Contact Post(Contact value)
   22:     {
   23:         Contact contact = _contacts.AddContact(value);
   24:         return contact;
   25:     }
   26:  
   27:     public void Put(string id, Contact value)
   28:     {
   29:         if (!_contacts.UpdateContact(id, value))
   30:         {
   31:             throw new HttpResponseException(HttpStatusCode.NotFound);
   32:         }
   33:     }
   34:  
   35:     public void Delete(string id)
   36:     {
   37:         if (!_contacts.RemoveContact(id))
   38:         {
   39:             throw new HttpResponseException(HttpStatusCode.NotFound);
   40:         }
   41:     }
   42: }

Note: We use IQueryable<Contact> as the return type of the Get() method. This enables automatic query-support using OData query syntax including $top and $skip. That is, if you use a URI like “/api/contacts?$top=2” with a “?$top=2” query component then you will only get the first two entries back.

Hosting the Controller

Now that we have the controller we can either host it in ASP or as selfhost. Here we use selfhost to host the controller in a simple console application but it would work exactly the same if hosted in ASP. As usual we create a HttpSelfHostConfiguration, add a route, then create a server, and start it:

    1: static void Main(string[] args)
    2: {
    3:     HttpSelfHostServer server = null;
    4:     try
    5:     {
    6:         // Set up server configuration
    7:         HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("https://localhost:8080");
    8:  
    9:         config.Routes.MapHttpRoute(
   10:             name: "DefaultApi",
   11:             routeTemplate: "api/{controller}/{id}",
   12:             defaults: new { id = RouteParameter.Optional }
   13:         );
   14:  
   15:         // Create server
   16:         server = new HttpSelfHostServer(config);
   17:  
   18:         // Start listening
   19:         server.OpenAsync().Wait();
   20:  
   21:         Console.WriteLine("Hit ENTER to exit...");
   22:         Console.ReadLine();
   23:  
   24:     }
   25:     finally
   26:     {
   27:         if (server != null)
   28:         {
   29:             // Stop listening
   30:             server.CloseAsync().Wait();
   31:         }
   32:     }
   33: }

Note: In order to successfully start the selfhost server you have to run as admin (or configure http.sys with the appropriate URI prefix using netsh).

Trying out the Controller

When running the controller can be accessed using any HTTP client. In the full sample I show how to use HttpClient to do GET and POST but Fiddler is often very useful for trying out various combinations of GET, PUT, POST, and DELETE by using the Composer tab manually to create requests. For example, you can create a POST request like this to insert a new Contact and then hit Execute:

AddContact

Similarly you can create a GET request to ask for the two first entries which will yield a result like this where the response body contains the first two Contacts:

GetTopContactsResult

As mentioned above, when you modify the data you can track the contents of the MongoDB database using MongoVUE.

Have fun!

Henrik

del.icio.us Tags: asp.net,webapi,mvc,rest,httpclient,mongodb