Code that accompanies this article can be downloaded here. (Notes: Tests in this code will pass only if MongoDB server is running on localhost:27017. Version 2.4.4 of MongoDB Driver is used).


In previous blog posts, Mongo DB basics part 1. and part 2. I covered some of the foundations of this database. In these articles, one can learn how to install MongoDB, create databases, collections, documents and how to use these entities. Also, there is a lot of information about distributing MongoDB, shards, and replica sets and how to manipulate with each of these entities. Readers can learn how this document NoSQL database functions, and what are perks of using it, as well as its downsides.

Nevertheless, there is no single line of code and everything is explained in more or less general manner. MongoDB Shell Client was used for demonstration of all crucial operations, so readers were not exposed to any coding lesions. That is why I’ll try to show here how MongoDB can be used from code, and do some of the operations, shown in previous articles, automatically. So, let’s take a look how MongoDB can be used in .NET, by implementing simple MongoDB CRUD Repository.

Installing MongoDB Driver

Guys from MongoDB provided a wide range of drivers for different programming languages. Drivers are just client libraries that application can use to communicate with Mongo Database. For .NET all we need to do is install NuGet package:

Install-Package MongoDB.Driver -Version 2.4.4

This will include three libraries in our project:

  • MongoDB.Bson – library used to serialize, deserialize and do other forms of manipulation with BSON documents.
  • MongoDB.Driver – the driver itself, used for communication with MongoDB.
  • MongoDB.Driver.Core – infrastructure on which driver has been built.

Mapping BSON to strongly typed C# object

If you checked out my previous posts, you’ve learned that MongoDB stores documents in BSON format. Those are basically binary JSON files (ok, it is a little bit more complicated than that :)). When we use .NET driver we consume documents trough BsonDocumentWhat we want to do in this exercise is to map those BsonDocuments to strongly typed C# objects. But before that, let’s first see how our database and its entities look like.

For purposes of this exercise, we will use database called – blog. This database has a collection – users, and this collection contains document for each user. JSON document for the individual user would look like something like this:

{
"_id" : ObjectId("59ce6b34f48f171624840b05"),
"name" : "Nikola",
"blog" : "rubikscode.net",
"age" : 30,
"location" : "Beograd"
}

An equivalent of this in C# looks like this:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoDb
{
/// <summary>
/// Class used in business logic to represent user.
/// </summary>
public class User
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("name")]
public string Name { get; set; }
[BsonElement("blog")]
public string Blog { get; set; }
[BsonElement("age")]
public int Age { get; set; }
[BsonElement("location")]
public string Location { get; set; }
}
}

view raw
User.cs
hosted with ❤ by GitHub

You will notice that there are attributes for each property of this class. These attributes allow us to automatically map data from BsonDocument to our class. BsonId is used to map unique document identifier. Every document in MongoDB has this element, and its type is ObjectIdBsonElement(field_name) is used to map other fields from the object on the object properties.

Since we know that document databases don’t have a strict schema, it is possible to have more fields in the JSON document then we really want to use in our application. Also, it is possible that we don’t want to pick up all the fields from the database. For this, we can use BsonIgnoreExtraElements attribute on the class itself.

Users Repository

The main goal of this exercise is to create a class which will give us the ability to do simple CRUD operations on users collection. The first thing we need to do is connect to the database from our application. The easiest way to do this is to use MongoClient class. It accepts connection string so we will have to provide this.

Alternatively, MongoClientSettings class can be used, which provide various possibilities. One of the interesting ones is ClusterConfiguration property, which is of ClusterBuilder type, used for configuring clusters. Other classes that we need to use are MongoDatabaseto access defined database (blog in this case), and MongoCollectionto access defined collection (users in this case). Here is how that looks like in the code:

/// <summary>
/// Class used to access Mongo DB.
/// </summary>
public class UsersRepository
{
private IMongoClient _client;
private IMongoDatabase _database;
private IMongoCollection<User> _usersCollection;
public UsersRepository(string connectionString)
{
_client = new MongoClient(connectionString);
_database = _client.GetDatabase("blog");
_usersCollection = _database.GetCollection<User>("users");
}
}

view raw
UserRepository.cs
hosted with ❤ by GitHub

Ok, this is pretty straightforward. Still, there is one thing that should be noted. MongoCollection is defined using Users as a template type, and this is possible only because we added those attributes to Users class. The other way to achieve this is by using BsonDocument, but then we have to map fields manually to properties of Users class.

Create Users

Inserting document in the database is done easily once previous steps have been followed:

public async Task InsertUser(User user)
{
await _usersCollection.InsertOneAsync(user);
}

view raw
InsertUsers.cs
hosted with ❤ by GitHub

Obviously, I have used asynchronous operation InsertOneAsync, but you can use synchronous one too. Again, because we mapped JSON fields on Users properties, it is easy to work with these operations.

Read Users

Reading users is done like this:

public async Task<List<User>> GetAllUsers()
{
return await _usersCollection.Find(new BsonDocument()).ToListAsync();
}
public async Task<List<User>> GetUsersByField(string fieldName, string fieldValue)
{
var filter = Builders<User>.Filter.Eq(fieldName, fieldValue);
var result = await _usersCollection.Find(filter).ToListAsync();
return result;
}
public async Task<List<User>> GetUsers(int startingFrom, int count)
{
var result = await _usersCollection.Find(new BsonDocument())
.Skip(startingFrom)
.Limit(count)
.ToListAsync();
return result;
}

view raw
ReadingUsers.cs
hosted with ❤ by GitHub

There are three different implementations of this functionality. Let’s go through each of them.

GetAllUsers returns all users from the database. We use the Find method of the MongoCollection class to do so, and pass empty BsonDocument into it. In the next method, GetUsersByField method, we can see that this Find method actually receives filter object, which it will use as criteria for getting the data.

In the first function, we use an empty filter and thus receive all users from the collection. In second, we use Builder to create the filter which will be used against the database. Finally, the last function – GetUsers uses Skip and Limit methods of MongoCollection to get a necessary chunk of data. For example, this last function can be used for paging.

Update Users

Documents can be updated in a similar manner:

public async Task<bool> UpdateUser(ObjectId id, string udateFieldName, string updateFieldValue)
{
var filter = Builders<User>.Filter.Eq("_id", id);
var update = Builders<User>.Update.Set(udateFieldName, updateFieldValue);
var result = await _usersCollection.UpdateOneAsync(filter, update);
return result.ModifiedCount != 0;
}

view raw
UpdateUser.cs
hosted with ❤ by GitHub

In this example, function UpdateOneAsync is used, but if you want to change values of multiple documents you can use UpdateMultipleAsync. Also, synchronous siblings of these operations also exist.

Another interesting fact is that using this function you can add fields that are not in the “schema”. For example, if you want to add a new field in the user document, you can do this:

var users = await _mongoDbRepo.GetUsersByField("name", "Nikola");
var user = users.FirstOrDefault();
var result = await _mongoDbRepo.UpdateUser(user.Id, "address", "test address");

view raw
AddField.cs
hosted with ❤ by GitHub

This way the big MongoDB’s (and rest of the document databases) feature of a modular schema is harnessed. You are not blocked by defined objects, you can store information dynamically if you need to do so.

Delete Users

And finally, users can be deleted this way:

public async Task<bool> DeleteUserById(ObjectId id)
{
var filter = Builders<User>.Filter.Eq("_id", id);
var result = await _usersCollection.DeleteOneAsync(filter);
return result.DeletedCount != 0;
}
public async Task<long> DeleteAllUsers()
{
var filter = new BsonDocument();
var result = await _usersCollection.DeleteManyAsync(filter);
return result.DeletedCount;
}

view raw
DeleteUsers.cs
hosted with ❤ by GitHub

Pretty straightforward, don’t you think? Same as in Reading examples, Builder was used for creating a filter.

Creating Indexes

Adding indexes is a bit different. If we want to do it dynamically, the easiest way is to use BsonDocument(. Here is how it is done:

public async Task CreateIndexOnCollection(IMongoCollection<BsonDocument> collection, string field)
{
var keys = Builders<BsonDocument>.IndexKeys.Ascending(field);
await collection.Indexes.CreateOneAsync(keys);
}

view raw
CreateIndex.cs
hosted with ❤ by GitHub

On the other hand, if we know what our indexes will be beforehand, we can use strongly typed implementation like this:

public async Task CreateIndexOnNameField()
{
var keys = Builders<User>.IndexKeys.Ascending(x => x.Name);
await _usersCollection.Indexes.CreateOneAsync(keys);
}

Conclusion

Here, in one simple example, we went through many of MongoDB Driver’s features. We learned how to map JSON documents to .NET objects, which gave us an easy way to use rest of the features. CRUD operations, which are most commonly used, have been showed and explained, too.

In general, one could see how to use MongoDB features with C# in .NET environment.

Read full MongoDB Serial here.


Read more posts from the author at Rubik’s Code.

Creative Commons License