.NET and MongoDB A Code First Introduction

John C. Zablocki
Development Lead, MagazineRadar
NYC Code Camp
We thank the following companies for their gracious
Platinum Sponsor
Gold Sponsors
Silver Sponsors
MongoDB Basic Concepts
MongoDB Shell
MongoDB C# Driver
MongoDB Design Considerations
In Depth: Meringue
Case Study: RateMySnippet
Drag and Drop Programming
PHP (Pretty Horrible Programming)
Visual Basic .NET
Regular Expressions
Relational Databases
Schema-less documents stored in collections
Documents are stored as BSON (Binary
JavaScript used to query and manipulate
documents and collections
Each document in a collection has a unique
BSON ObjectId field named _id
Collections belong to a database
Download the binaries from mongodb.org
Extract to Program Files directory (or wherever)
Create a directory c:\data\db
Run mongod.exe from the command line with
the --install switch
 See http://bit.ly/aed1RW for some gotchas
 To run the daemon without installing, simply run
mongod.exe without arguments
 Run mongo.exe to verify the daemon is running
The MongoDB interactive JavaScript shell
(mongo.exe) is a command line utility for
working with MongoDB servers
Allows for CRUD operations on collections
May be used for basic administration
Creating indexes
Cloning databases
Also useful as a test-bed while building apps
This script file demonstrates the basics of MongoDB from the interactive shell.
It's not intended to be a best-practive example for anything!
/*Connect to a server:port/database
(defaults are localhost:27017/test ):*/
mongo.exe localhost:27017/AltNetGroup
//Switch database:
use CodeCamp
//View collections in a database:
show collections
//create an index on Name field
db.Posts.ensureIndex({ Name : 1 });
//copy one database to another
db.copyDatabase("CodeCamp", "AltNetGroup")
//create a document
var post = { Title: "On Installing MongoDB as a Service on Windows" }
//insert a document, if the collection doesn't exist it's created
//verify that the document was created
//write a query to find a post with a valid title
var query = { Title: { $ne: null} }
//use that query to find the post
var post = db.Posts.findOne(query);
//this line will actually set the content after pressing enter
post.Content = "When installing MongoDB as a service on Windows..."
//update the content to include an author using collection update method
db.Posts.update( { Title : "On Installing MongoDB as a Service on Windows" }, { Author :
"John Zablocki" } )
//check that the post was updated
//where'd my document go? updates are in place, replacing entire docs!
//need to use the $set operator to update partial documents - start over
//first remove the new document. Notice remove takes a function argument.
//find and findOne also accept functions as arguments
db.Posts.remove(function (e) { return this.Author == "John Zablocki" })
//rerun the first statements up to but not including the db.Posts.update(...
db.Posts.update({ Title: "On Installing MongoDB as a Service on Windows" },
{ $set: { Author: "John Zablocki" } })
//verify that the update worked
//add two more tags
{ Title: "On Installing MongoDB as a Service on Windows" },
{ $pushAll: { Tags: ["windows", "nosql"] } })
//add another post
{ Author : "John Zablocki", Title : "On MapReduce in MongoDB",
Tags: ["mongodb", "nosql"]
//verify that last insert worked
db.Posts.findOne(function (e) { return this.Title.indexOf("MapReduce") != -1; })
//add a "like" counter to the post. The boolean arguments tell //update not to
insert if the document doesn't exist and to //update all documents, not just one
db.Posts.update({ Author: "John Zablocki" }, { $set: { Likes: 0} }, false, true)
//increment the likes counter for the mapreduce article
db.Posts.update({ Title: /mapreduce/i }, { $inc: { Likes: 1} })
//check that the counter was incremented
db.Posts.findOne({ Title: /mapreduce/i }).Likes
//use MapReduce to get counts of the tags
create the map and reduce functions
var map = function() {
if (!this.Tags) { return; }
for (var index in this.Tags) {
emit(this.Tags[index], 1);
//conceptually, reduce gets called like:
//reduce("mvc", [1, 1]);
//reduce("norm", [1]
var reduce = function(key, vals) {
var count = 0;
for (var index in vals) {
count += vals[index];
return count;
run the mapreduce command on the Posts collection
using the map and reduce functions defined above
store the results in a collection named Tags
var result = db.runCommand(
mapreduce : "Posts",
map : map,
reduce : reduce,
out : "Tags"
//first, insert some data
db["UserActions"].insert({ Username : "jzablocki", Action : "Login"})
db["UserActions"].insert({ Username : "jzablocki", Action : "Login"})
db["UserActions"].insert({ Username : "jzablocki", Action : "Login"})
db["UserActions"].insert({ Username : "jzablocki", Action : "PasswordChange"})
db["UserActions"].insert({ Username : "mfreedman", Action : "PasswordChange"})
db["UserActions"].insert({ Username : "mfreedman", Action : "PasswordChange"})
db["UserActions"].insert({ Username : "mfreedman", Action : "Login"})
//now run the group by
{ key : { Username : true, Action : true },
cond : null,
reduce : function(ojb, prev) { prev.count++; },
initial: { count: 0 }
A funny thing happened on the way to Philadelphia…
10gen developed and supported
Consists of two primary components, a
BSON serializer and the MongoDB driver
Support for typed and untyped collections,
MapReduce, and all CRUD operations
Currently lacking a LINQ provider
Current version (as of 2/10/11) is 0.11.x
private static MongoDatabase _mongoDatabase = null;
static Program() {
//MongoServer manages access to MongoDatabase
MongoServer mongoServer =
//MongoDatabase used to access MongoCollection instances
_mongoDatabase = mongoServer.GetDatabase("CodeCamp");
var artist = new Artist() { Name = "The Decembrists" };
//Inserting a document into a typed collection
//Updating (replacing) a document in a typed collection
artist.Name = "The Decemberists";
//Updating a nested collection
Query.EQ("Name", "The Decemberists"),
Update.PushAll("Albums", "Castaways and Cutouts",
"Picaresque", "Hazards of Love", "The Crane Wife")
//Find all documents in a typed collection
var artists =
Console.WriteLine("Artist name: " +
//Query with a document spec
var artist =
Query.EQ("Name", "The Decemberists"));
Console.WriteLine("Album count: " + artist.Albums.Count);
//Count the documents in a collection
long count =
Console.WriteLine("Document count: " + count);
var artists = _mongoDatabase.GetCollection<Artist>(COLLECTION);
//Find items in typed collection
var artistsStartingWithThe = artists.Find(Query.Matches("Name",
new Regex("the", RegexOptions.IgnoreCase)));
Console.WriteLine("First artist starting with The: " +
//Find artists without pulling back nested collections
var artistsWithDecInTheName =
artists.Find(Query.Matches("Name", "Dec")).SetFields("Name");
Console.WriteLine("First artist with dec in name: " +
////Find artists with a given tag
var artistsWithIndieTag = artists.Find(Query.In("Tags",
Console.WriteLine("First artist with indie tag: " +
//Add some tags
Query.EQ("Name", "The Decemberists"),
Update.PushAll("Tags", "Folk rock",
var artist = new Artist() {
Name = "Sunny Day Real Estate",
Albums = new List<string>() { "How it Feels to
be Something On", "Diary" },
Tags = new List<string>() { "Indie", "Emo" }
//Create map and reduce functons
BsonJavaScript map = @"function() {
if (!this.Tags ) { return; }
for (index in this.Tags) { emit(this.Tags[index], 1); }
BsonJavaScript reduce = @"function(previous, current) {
var count = 0;
for (index in current) { count += current[index]; }
return count;
var result =
_mongoDatabase.GetCollection<Artist>(COLLECTION).MapReduce(map, reduce,
var collection =
Console.WriteLine("Tag count: " + collection.Count());
//add one more artist for good measure
var artists = _mongoDatabase.GetCollection<Artist>(COLLECTION);
artists.Insert(new Artist() { Name = "Blind Pilot", Albums = new
List<string>() { "3 Rounds and a Sound" } });
BsonJavaScript reduce =
@"function(obj, out) { out.count += obj.Albums.length; }";
var groupBy = _mongoDatabase.GetCollection<Artist>(COLLECTION)
.Group(Query.Null, GroupBy.Keys("Name"),
new BsonDocument("count", 1), reduce, null);
foreach (var item in groupBy) {
Console.WriteLine("{0}: {1} Album(s)",
item.GetValue(0), item.GetValue(1));
Your object graph is your data model
Don't be afraid to store data redundantly
 Your graph might be redundant!
Not everything has to fit in 1 document
Don't be afraid to store aggregate statistics
with a document.
Generally speaking, most MongoDB drivers will
serialize an object graph as a single document
 The relationships of your classes creates an implied
 Migrating this schema is not trivial if you are trying to
deserialize properties that did not or no longer exist
Consider use cases carefully to avoid inefficiently
structured documents
Projection queries will be your friend
Optimize documents for quick reads and
Your application layer will have to maintain
referential integrity!
If every time you access a Post document,
you need some of an Author document's
data, store that data with Post
Design simple classes for this redundant data
for reusability (see AuthorInfo in Meringue)
Nothaving formal relationships does not
mean throwing away relationships
Consider a user and his or her logged actions
 The user would likely have a User class/doc with
properties for name, email, etc.
 User actions are generally write heavy and read
out of band.
 Don't clutter user documents - create a separate
collection for user actions
The schema-less nature of documents makes
it easy to store meta data about that
document – particularly aggregate data
Consider a blog post with a rating feature
 Each rating would be stored as a nested
document of the post
 Rather than compute vote totals and averages
real time, simply add these properties to the
document and update on writes
Eat food. Not too much. Mostly Plants.
- Michael Pollan
Write code. Not too much. Mostly C#.
- John Zablocki
http://dllHell.net - my blog
http://www.CodeVoyeur.com - my code
http://mongodb.org - Official MongoDB site

similar documents