Quick Start C# and MongoDB - Read Operations

Ken W. Alger

#C#/.NET
C Sharp Quick Start

Welcome to the latest installment of the Quick Start C# and MongoDB series where we take on the second letter of CRUD. R is for Read. So far we've looked at how to connect to a MongoDB Atlas cluster using C#, and then how to Create data in a collection. Now, we move on to reading that data.

Created Data

Just to recap, in the previous post on Create, I inserted the following BSONDocument into the sample_training.grades collection from the MongoDB sample dataset.

var document = new BsonDocument
            {
                { "student_id", 10000 },
                { "scores", new BsonArray
                    {
                    new BsonDocument{ {"type", "exam"}, {"score", 88.12334193287023 } },
                    new BsonDocument{ {"type", "quiz"}, {"score", 74.92381029342834 } },
                    new BsonDocument{ {"type", "homework"}, {"score", 89.97929384290324 } },
                    new BsonDocument{ {"type", "homework"}, {"score", 82.12931030513218 } }
                    }
                },
                { "class_id", 480}
            };

Read Operations

To Read documents in MongoDB, we use the Find() method. This method allows us to chain a variety of methods to it, some of which I'll explore in this post. To get the first document in the collection, we can use the FirstOrDefault or FirstOrDefaultAsync method, and print the result to the console.

var firstDocument = collection.Find(new BsonDocument()).FirstOrDefault();
Console.WriteLine(firstDocument.ToString());

returns...

{ "_id" : ObjectId("56d5f7eb604eb380b0d8d8ce"), 
"student_id" : 0.0, 
"scores" : [
{ "type" : "exam", "score" : 78.404463095042658 },
{ "type" : "quiz", "score" : 73.362247832313386 }, 
{ "type" : "homework", "score" : 46.980982486720535 }, 
{ "type" : "homework", "score" : 76.675561386562222 }
], 
"class_id" : 339.0 }

You may wonder why we aren't using Single as that returns one document too. Well, that has to also ensure the returned document is the only document like that in the collection and that means scanning the whole collection.

Reading with a Filter

Let's find the document we created and print it out to the console. The first step is to create a filter to query for our specific document.

var filter = Builders<BsonDocument>.Filter.Eq("student_id", 10000);

Here we're setting a filter to look for a document where the student_id is equal to 10000. We can pass the filter into the Find() method to get the first document that matches the query.

var studentDocument = collection.Find(filter).FirstOrDefault();
Console.WriteLine(studentDocument.ToString());

returns...

{ "_id" : ObjectId("5d88f88cec6103751b8a0d7f"), 
"student_id" : 10000, 
"scores" : [
{ "type" : "exam", "score" : 88.123341932870233 }, 
{ "type" : "quiz", "score" : 74.923810293428346 }, 
{ "type" : "homework", "score" : 89.979293842903246 }, 
{ "type" : "homework", "score" : 82.129310305132179 }
], 
"class_id" : 480 }

If a document isn't found that matches the query, the Find() method returns null. Finding the first document in a collection, or with a query is a frequent task. However, what about situations when all documents need to be returned, either in a collection or from a query?

Reading All Documents

For situations in which the expected result set is small, the ToList() or ToListAsync() methods can be used to retrieve all documents from a query or in a collection.

var documents = collection.Find(new BsonDocument()).ToList();

We can iterate over that list and print the results like so:

 foreach(BsonDocument doc in documents)
 {
     Console.WriteLine(doc.ToString());
 }       

Filters can be passed in here as well, for example, to get documents with exam scores equal or above 95. The filter here looks slightly more complicated, but thanks to the MongoDB driver syntax, it is relatively easy to follow. We're filtering on documents in which inside the scores array there is an exam subdocument with a score value greater than or equal to 95.

var highExamScoreFilter = Builders<BsonDocument>.Filter.ElemMatch<BsonValue>(
"scores", new BsonDocument { { "type", "exam" },
{ "score", new BsonDocument { { "$gte", 95 } } }
});
var highExamScores = collection.Find(highExamScoreFilter).ToList();

Where there's more than a small list of results you need it retrieve, the Find method can be made to return a cursor with ToCursor which points to the records that need to be retrieved. This cursor can be used in a foreach statement in a synchronous situationsby using the ToEnumerable adapter method.

var cursor = collection.Find(highExamScoreFilter).ToCursor();
foreach (var document in cursor.ToEnumerable())
{
     Console.WriteLine(document);
}

This can be accomplished asynchronously with the ForEachAsync method:

await collection.Find(highExamScoreFilter)
     .ForEachAsync(document => Console.WriteLine(document));

Sorting

With many documents coming back in the result set, it is often helpful to sort the results. We can use the Sort() method to accomplish this to see which student had the highest exam score. Note that the sort method asks the database to do the sorting; it's not performed by the driver or application.

var sort = Builders<BsonDocument>.Sort.Descending("student_id");

var highestScores = collection.Find(highExamScoreFilter).Sort(sort);

And we can append the First() method to that to just get the top student.

var highestScore = collection.Find(highExamScoreFilter).Sort(sort).First();

Console.WriteLine(highestScore);

Based on the Atlas Sample Data Set, the document with a student_id of 9997 should be returned with an exam score of 95.441609472871946.

You can see the full code for both the Create and Read operations I've shown in the gist here.

Wrap Up

The C# Driver for MongoDB provides many ways to Read data from the database and supports both synchronous and asynchronous methods for querying the data. By passing a filter into the Find() method, we are able to query for specific records. The syntax to build filters and query the database is straightforward and easy to read, making this step of CRUD operations in C# and MongoDB simple to use.

Next up in this C# Quick Start CRUD operation series, I'll take a look at how to Update documents. Once again, the C# Driver for MongoDB provides a nice interface for this step and both sync and async versions.

Get started with an M0 cluster on MongoDB Atlas today. It's free forever and you'll be able to work alongside this blog series.

Previously in the Quick Start C# and MongoDB series: