Click here to Skip to main content
13,664,632 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

17.4K views
25 bookmarked
Posted 2 Oct 2017
Licenced CPOL

Using MongoDB in C#

, 19 Oct 2017
Rate this:
Please Sign up or sign in to vote.
Learn how to use MongoDB in C# following implementation of simple repository class

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 at 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 through BsonDocument. What 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 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; }
    }
}

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 ObjectId. BsonElement(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 than 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 provides 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 MongoDatabase, to access defined database (blog in this case), and MongoCollection, to 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");
    }
}

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 the previous steps have been followed:

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

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;
}

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 the 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;
}

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");

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;
}

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);
}

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 MongoDB Serial here.


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

Creative Commons License


 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nikola M. Živković
Software Developer (Senior) Vega IT Sourcing
Serbia Serbia
Read more at my blog: https://rubikscode.net

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Questionformat error while retrieving Bson document Pin
Member 1351521417-Jun-18 5:08
memberMember 1351521417-Jun-18 5:08 
QuestionExamples with complex objects Pin
Tate Antrim4-Oct-17 7:13
professionalTate Antrim4-Oct-17 7:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04-2016 | 2.8.180810.1 | Last Updated 20 Oct 2017
Article Copyright 2017 by Nikola M. Živković
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid