Kash Farooq's software development blog

.NET Developer

Posts Tagged ‘NoSQL’

MongoDB in C#: Mapping IDs

Posted by Kash Farooq on March 4, 2012

In my previous post about C# and MongoDB, I polluted my domain model with a MongoDB type:

public class Movie
{
  public BsonObjectId Id { get; set; }
  public string Title { get; set; }
  public string Year { get; set; }

  etc
}

When you Insert a document into MongoDB using the C# driver, the driver needs to check to see if the Id has a value. It assigns one if it doesn’t. The above code worked because I used a MongoDB C# driver ID type – the driver could work out what my ID property is. But I don’t really want to use BsonObjectId in this class – it ties me to MongoDB. Instead, you can use a built-in .NET type for the ID property and tell the MongoDB C# driver about it.

First, let’s change the type of the Id property. I’ve gone for a string but there are other types you could use:

public class Movie
{
  public string Id { get; set; }
  public string Title { get; set; }
  public string Year { get; set; }

  etc
}

Now, you need to map that property. I’m doing this code in an NUnit test, so I’m going to do the mapping in the TestFixtureSetUp:

[TestFixtureSetUp]
public void TestFixtureSetUp()
{
  BsonClassMap.RegisterClassMap<Movie>(cm => {
                                              cm.AutoMap();
                                              cm.SetIdMember(cm.GetMemberMap(x => x.Id).SetIdGenerator(StringObjectIdGenerator.Instance));
                                             }
                                       );
}

So, I’ve told the MongoDB C# driver that the ID column is Movie.Id and that it needs to use its built in StringObjectIdGenerator to generate IDs.

There are several ID generators built into the driver:

  • BsonObjectIdGenerator
  • CombGuidGenerator
  • GuidGenerator
  • NullIdChecker
  • ObjectIdGenerator
  • StringObjectIdGenerator
  • ZeroIdChecker<T>

Read the CSharp Driver Serialization Tutorial for more information about these generators and mapping them to properties.

Advertisements

Posted in .NET, NoSQL | Tagged: , , , | Leave a Comment »

Getting started with MongoDB in C#

Posted by Kash Farooq on February 18, 2012

I thought I’d play with NoSQL. There are lots of implementations to choose from, but I’ve decided to start with MongoDB. Especially after seeing the impressive list of real-life production MongoDB deployments.

The MongoDB server and data structure is organised as follows:

  • A Mongo server holds a set of databases
  • A database holds a set of collections
  • A collection holds a set of documents
  • A document is a set of fields
  • A field is a key-value pair
  • A key is a string
  • A value is a
    • strings, integers, etc.
    • another document
    • an array of values

Right. Let’s get started. First, download the two things you’ll need to start developing MongoDB apps in .NET: the database software and the C# driver.

I also recommend you download a PDF of the documentation. It is available as a MongoDB docs daily build. This PDF contains everything: set-up instructions, tutorials, advice and language specific driver documentations.

Use the Windows Quick Start to get going and test your MongoDB server with the administrative JavaScript shell client. I also advise you go through the tutorial – it walks you through more complex examples using the administrative shell client. As noted above, both these guides are in the MongoDB Docs PDF.

Install the C# driver by just running the MSI. It installs to C:\Program Files (x86)\MongoDB.

Create a .NET project and add assembly references to MongoDB.Driver.dll and MongoDB.Bson.dll. I created NUnit integration tests to test MongoDB.

First, I created a class that I wanted to store as a MongoDB document:

public class Movie
{
  public BsonObjectId Id { get; set; }
  public string Title { get; set; }
  public string Year { get; set; }
  public List<string> Actors { get; set; }

  public void AddActor(string actor)
  {
    if (Actors == null)
    {
      Actors = new List<string>();
    }
    Actors.Add(actor);
  }
}

Things to note in this class:

  • I’ve used the type BsonObjectId. To get up and running quickly, I basically told the MongoDB C# driver which property to use as an ID. You don’t have to do this. To see how to remove MongoDB C# driver specific types from your domain object, see my Mapping IDs post.
  • I’ve included a generic list – I wanted to see how the C# driver copes with this, how MongoDB stores this data, and how we can search for documents using this data.
  • I had to make the Actors list public, otherwise the C# driver wouldn’t store it. Perhaps there is a way to force the driver to store it without making it public? Something to research.

Next, let’s open a connection to a database and insert some data. I’m orchestrating the prerequisites for my test through a NUnit Setup method the connects to the database, empties the collection and inserts some fresh data:

private MongoServer server;
private MongoDatabase moviesDatabase;

[SetUp]
public void SetUp()
{
  Connect();
  Clean();
  InsertData();
}

private void Connect()
{
  server = MongoServer.Create();
  moviesDatabase = server.GetDatabase("movies_db");
}

As you can see, the connect method didn’t use any information such as connection strings, usernames, etc. It just calls Create. If you don’t provide any parameters it connects to a default instance of MongoDB on localhost. If you’ve changed any of the default settings, you’ll need a connection string.

The GetDatabase method gets me an instance of the “movies” database if it exists, or lazily creates it if it doesn’t (i.e. it won’t actually be created until you save some data to it).

The Clean() method is just used to empty the collection each time a test runs. I’m calling the RemoveAll method to empty all elements from the movies_collection:

private void Clean()
{
  var moviesCollection = moviesDatabase.GetCollection<Movie>("movies_collection");
  moviesCollection.RemoveAll();
}

Now let’s add some data to the movies_collection and save it to the movies_db:

private void InsertData()
{
  //Create some data
  var movie1 = new Movie {Title = "Indiana Jones and the Raiders of the Lost Ark", Year = "1981"};
  movie1.AddActor("Harrison Ford");
  movie1.AddActor("Karen Allen");
  movie1.AddActor("Paul Freeman");

  var movie2 = new Movie {Title = "Star Wars: Episode IV - A New Hope", Year = "1977"};
  movie2.AddActor("Mark Hamill");
  movie2.AddActor("Harrison Ford");
  movie2.AddActor("Carrie Fisher");

  var movie3 = new Movie {Title = "Das Boot", Year = "1981"};
  movie3.AddActor("Jürgen Prochnow");
  movie3.AddActor("Herbert Grönemeyer");
  movie3.AddActor("Klaus Wennemann");

  //Insert the movies into the movies_collection
  var moviesCollection = moviesDatabase.GetCollection<Movie>("movies_collection");
  moviesCollection.Insert(movie1);
  moviesCollection.Insert(movie2);
  moviesCollection.Insert(movie3);
}

So, we’ve created/opened a database called movie_db and insert some data into a movies_collection. Let’s check it’s there – get the collection and check that there are 3 movies in it.

[Test]
public void ShouldFindThreeMoviesInTheCollection()
{
  var moviesCollectionRetrieved = moviesDatabase.GetCollection<Movie>("movies_collection");
  Assert.That(moviesCollectionRetrieved.Count(), Is.EqualTo(3));
}

Now let’s do a query on the movie data. Let’s look for movies that were released in 1981, i.e. perform a query based on one of the keys – in this case, the “Year” key:

[Test]
public void ShouldFindTwoMoviesThatWereMadeIn1981()
{
  var moviesCollectionRetrieved = moviesDatabase.GetCollection<Movie>("movies_collection");

  QueryComplete findMoviesMadeIn1981Query = Query.EQ("Year", "1981");
  var moviesFound = moviesCollectionRetrieved.FindAs<Movie>(findMoviesMadeIn1981Query);

  Assert.That(moviesFound.Count(), Is.EqualTo(2));
  Assert.That(moviesFound.Count(movie => movie.Title == "Das Boot"), Is.EqualTo(1));
  Assert.That(moviesFound.Count(movie => movie.Title == "Indiana Jones and the Raiders of the Lost Ark"),Is.EqualTo(1));
}

Note that I used the generic version FindAs method – to get the C# driver to create movie objects from the MongoDB documents.

You can also very easily perform queries based on values stored in lists. Here, for example, I am looking for documents that contain a certain value in the Actors list – let’s look for movies that Harrison Ford has been in:

[Test]
public void ShouldFindTwoMoviesThatHarrisonFordHasBeenIn()
{
  var moviesCollectionRetrieved = moviesDatabase.GetCollection<Movie>("movies_collection");

  var findMoviesWithHarrisonFordInThem = Query.EQ("Actors", "Harrison Ford");
  var moviesFound = moviesCollectionRetrieved.FindAs<Movie>(findMoviesWithHarrisonFordInThem);
  Assert.That(moviesFound.Count(), Is.EqualTo(2));
  Assert.That(moviesFound.Count(movie => movie.Title == "Star Wars: Episode IV - A New Hope"),Is.EqualTo(1));
  Assert.That(moviesFound.Count(movie => movie.Title == "Indiana Jones and the Raiders of the Lost Ark"),Is.EqualTo(1));
}

Very neat.

In the code above I’ve been using Count(). That does not mean that the C# driver supports delayed execution and LINQ. The official statement is: “Version 1.0 of the official C# driver does not yet support LINQ”. In fact, the documentation states that for the following code, the query is sent to the server twice (once for FirstOrDefault and once for LastOrDefault)


var query = Query.EQ("author", "Ernest Hemingway");
var cursor = books.Find(query);
var firstBook = cursor.FirstOrDefault(); //query executed twice on the server
var lastBook = cursor.LastOrDefault(); //once for each of these IEnumerable<T> extension methods.

Finally, let’s look at how the data has been stored using the administrative JavaScript shell client, mongo.exe.

With the client, I’ll connect to the server, switch to the movies database and then display all the documents in the movies_collection:


MongoDB shell version: 2.0.2
connecting to: test
> use movies_db
switched to db movies_db
> db.movies_collection.find().forEach(printjson);
{
 "_id" : ObjectId("4f3f82a1d5c8851b60430b9a"),
 "Title" : "Indiana Jones and the Raiders of the Lost Ark",
 "Year" : "1981",
 "Actors" : [
 "Harrison Ford",
 "Karen Allen",
 "Paul Freeman"
 ]
}
{
 "_id" : ObjectId("4f3f82a1d5c8851b60430b9b"),
 "Title" : "Star Wars: Episode IV - A New Hope",
 "Year" : "1977",
 "Actors" : [
 "Mark Hamill",
 "Harrison Ford",
 "Carrie Fisher"
 ]
}
{
 "_id" : ObjectId("4f3f82a1d5c8851b60430b9c"),
 "Title" : "Das Boot",
 "Year" : "1981",
 "Actors" : [
 "J├╝rgen Prochnow",
 "Herbert Gr├Ânemeyer",
 "Klaus Wennemann"
 ]
}
>

Posted in .NET, NoSQL | Tagged: , , , | Leave a Comment »