Introduction
MongoDB is an appealing applicant in NoSQL world, providing much support with drivers, comprehensive documents, and a considerably large community to build applications
using NoSQL technology instead of going directly to RDBMSs such as MS SQL Server and Oracle.
In this article, I would like to describe how to use MongoDB with the official C# driver. There are a few alternatives to this driver, some of which are even considered better,
providing extra features such as LINQ support, but I prefer using this driver in my projects because it is supported officially.
The code samples are taken from the demo project. I recommend you to examine the demo project because you will understand better and have the option to try MongoDB.
A few words about the MongoDB data structure
In MongoDB, the data structure and hierarchy are like:
Databases
Collections
Documents
Fields
A field is a key-value pair. A key is a name (string). A value is a basic type like string, integer, float, timestamp, binary, etc., or a document, or an array of values.
In short, collections are made of JSON-like documents. This is the biggest difference from traditional RDBMSa, so try to treat your data as they are real objects, not
pure data. Also, you will see the classes starting with Bson
in the official driver.
The definition of BSON from bsonspec.org is a binary-encoded serialization of JSON-like documents. BSON is designed to be lightweight, traversable, and efficient.
BSON, like JSON, supports the embedding of objects and arrays within other objects and arrays.
Preparation
Firstly, download an appropriate MongoDB package from the following address and execute mongod.exe to start your MongoDB server.
If you have problems in starting your server related with paths, create a folder to store database files and start mongod.exe with the dbpath argument by
giving the folder path as shown in the following line:
mongod.exe --dbpath "C:\data"
In order to use this driver, download the required assembly files from the following link:
This is the place where you can obtain the source code and download the setup package. When you download a proper package and install it, you will be able to find the following
two assembly files under the path Program Files Folder\MongoDB\CSharpDriver 1.2. By the way, I used CSharpDriver-1.2.0.4274.msi in this article.
- MongoDB.Bson.dll
- MongoDB.Driver.dll
You need to add these two files as reference in your project. MongoDB.Bson.dll is for BSON serialization and the other is for database communication.
Now, you are ready to develop your project using MongoDB.
Server startup
Firstly, you should make sure that MongoDB is running and you point to the port on which the server is running correctly. Below you see the output when you start the MongoDB server from
the command line.
In the screen, the port is marked with a red box. Then, start the MongoDB console by running mongo.exe and run the following lines to create your database:
use MessageDB
Now you are finished with the MongoDB server.
Application
We would use these two namespaces to communicate with our MongoDB server and data to process, so add the following lines:
using MongoDB.Driver;
using MongoDB.Bson;
MongoServerSettings settings = new MongoServerSettings();
settings.Server = new MongoServerAddress("localhost", 27017);
MongoServer server = new MongoServer(settings);
var database = server.GetDatabase("MessageDB");
In these lines, we create a server object and pass the required settings such as credentials, connection string, port, etc. In this example, we use localhost as the database server
and 27017 as the server port. Then, we access the database, MessageDB, which we created before. With the database object, we are ready for CRUD operations.
Reading
var users = database.GetCollection("users").
FindAll().
SetSortOrder(SortBy.Descending("createdate"));
The official driver provides many ways to access and process the data. In the lines above, we first access the users
collection in ordered form using the createdate
value. It is also possible to access the collection or other compatible collections by indexing [] like datasase["users"]
.
foreach (var user in users)
{
var userName = user["firstname"].AsString;
...
}
After we get the results, either in BSON or strongly typed object format, we can iterate over them.
It is possible to write complex queries using Query
.
var query = Query.And(
Query.Matches("firstname", BsonRegularExpression.Create("^e")),
Query.GT("createdate", BsonValue.Create(DateTime.Now.AddDays(-7).Date)));
var users = database.
GetCollection("users").
Find(query).
SetSortOrder(SortBy.Descending("createdate"));
You can obtain the query transformed from objects to MongoDB notations by evaluation like this:
Editing
When we get the data, we can edit the values directly and save the updated documents by calling the Save
method of the relevant collection. In the following
lines, we obtain a unique document by providing the ID value.
var user = users.FindOneById(ObjectId.Parse(id));
user["firstname"] = userToEdit.FirstName;
user["lastname"] = userToEdit.LastName;
user["age"] = userToEdit.Age;
users.Save(user);
The ID value is a special type of field which is created by the database itself to provide uniqueness of each document, so it is possible to get the documents easily. The ID value
actually contains a string value like 4ea41ac244b8681c3072b212, so it is easy to pass this value through each request.
The FindOneById
method gets a BsonValue
. In order to pass the ID value as BsonValue
, we call ObjectId.Parse
to obtain an appropriate format
of BsonValue
for ID values.
Lastly, we call Save
to update the edited document.
Insertion
Adding new documents is much simple as editing them. You create a reference for the collection, create a new BsonDocument
, and add it to the collection
reference by calling insert
.
var users = database.GetCollection("users");
var user = new BsonDocument();
user["firstname"] = userToEdit.FirstName;
user["lastname"] = userToEdit.LastName;
user["age"] = userToEdit.Age;
user["createdate"] = DateTime.Now;
users.Insert(user);
Dynamic schema
Up to now, we considered basic objects and dealt with CRUD operations on them. As you can see, we did not define any schema specific setting yet. When we set a value
in BsonDocument
, the field key is created automatically with the type of given value. In the following case, when we execute the following line:
user["age"] = 18;
The age
key is created automatically and the type of the field would be Int32
. You also have the option to check if the key exists by calling
the Contains
method of the object and making sure that it is compatible with a given type and will be cast properly, like:
if (user.Contains("updatedate") && user["updatedate"].IsDateTime)
{
var updatedate = user["updatedate"].AsDateTime;
}
I mentioned dynamic schema and I want to make my user object more dynamic:
[HttpPost]
public ActionResult Index(string id, string newRemark)
{
var users = database.GetCollection("users");
var user = users.FindOneById(ObjectId.Parse(id));
var remark = new BsonDocument().
Add("content", newRemark).
Add("date", DateTime.Now);
if (user.Contains("remarks"))
{
user["remarks"].AsBsonArray.Add(BsonValue.Create(remark));
}
else
{
user["remarks"] = new BsonArray().Add(BsonValue.Create(remark));
}
users.Save(user);
return RedirectToAction("Index", new { id = id });
}
In these lines, we change the structure of our user object. We propose the new data type, remarks
.
First, we check if the user object already contains an element with the key remarks
. remarks
actually is an array which holds content
and date
values. If remarks
exists, we cast it to an array and add a new remark. Otherwise, we create a new type of array and add the new remark to it. That's all.
The output of the user object will be like this:
{
"_id" : ObjectId("4ea51941073d601758138560"),
"firstname" : "ercan",
"lastname" : "anlama",
"age" : 26,
"createdate" : ISODate("2011-10-24T07:52:33.29Z"),
"remarks" :
[
{
"content" : "this is my first remark",
"date" : ISODate("2011-10-24T07:53:22.511Z")
}
]
}
Object serialization
The driver supports object serialization for documents. It means that you do not need to access the fields and values with string values, but strongly typed values. Let's see this in action.
We have two objects, User
and Remark
.
public class User
{
public User()
{
Remarks = new List<Remark>();
}
public ObjectId id { get; set; }
[BsonElementAttribute("firstname")]
public string FirstName { get; set; }
[BsonElementAttribute("lastname")]
public string LastName { get; set; }
[BsonElementAttribute("age")]
public int Age { get; set; }
[BsonElementAttribute("createdate")]
public DateTime CreateDate { get; set; }
[BsonElementAttribute("remarks")]
public IList<Remark> Remarks { get; set; }
public string GetFullName()
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public class Remark
{
[BsonElementAttribute("content")]
public string RemarkContent;
[BsonElementAttribute("date")]
public DateTime Date;
}
We use BsonAttribute
s to state which property in the object matches which field in BsonDocument
. Also, mind that we define a special property which is
the ObjectId
type, to store ID values of BsonDocument
s. If the property name is the same as
the field key, you do not need to state any attribute as we did in the id
property.
Here is how to implement this serialization:
var user = database.GetCollection("users").FindOneByIdAs<User>(ObjectId.Parse(id));
The driver employs the methods which end in As
. In general, these methods serve to retrieve documents in strongly typed format. Therefore, we can keep
the bridge between NoSQL data and object orientation.
Final words
I tried to introduce in this article what is MongoDB and how to use the official C# driver. Of course, these are the basic points, but the most commonly used functions of MongoDB,
so it is larger than illustrated in this article. It offers powerful functionalities like GridFS and MapRedure, which you might need in some areas while developing your project with MongoDB.