DEV Community

Cover image for Tutorial: MongoDB With C# The Easy Way!
Dĵ ΝιΓΞΗΛψΚ
Dĵ ΝιΓΞΗΛψΚ

Posted on • Updated on

Tutorial: MongoDB With C# The Easy Way!

the goal of this article is to familiarize you with an alternative, less verbose & convenient way to store and retrieve data in MongoDB server compared to what you'd typically see with such tutorials. hopefully by the end of this article you'll see the light and decide to ditch SQL Server & Entity Framework and get onboard the mongo train.

Install MongoDB server

if you don't already have mongodb server running on your machine, please follow the tutorial below before moving forward.

Scaffold a console application project

create a new console application project using visual studio targetting .net core or enter the following in a powershell/cmd window:

dotnet new console -n LearnMongo
start .\LearnMongo\LearnMongo.csproj
Enter fullscreen mode Exit fullscreen mode

Install Dependencies

open the package manager console and enter the following:
tip: tools > nuget package manager > package manager console

Install-Package MongoDB.Entities
Enter fullscreen mode Exit fullscreen mode

Initialize Database Connection

open Program.cs file and make it look like the following:

using MongoDB.Entities;
using System.Threading.Tasks;

namespace LearnMongo
{
    static class Program
    {
        private async static Task Main()
        {
            await DB.InitAsync("MyDatabase", "localhost", 27017);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

we're specifying that this application should store our data in a database called MyDatabase in the mongodb server running on localhost listening on the default port.

Saving Entities

add a new class file called Person.cs to the project and make it look like the following:

using MongoDB.Entities;
using System;

namespace LearnMongo
{
    public class Person : Entity
    {
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
        public int SiblingCount { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

main/root entities you want persisted to mongodb must inherit from the class Entity which is supplied by the package we installed earlier.

then go back to the Program.cs file and add the following below the db initialization line:

var lisa = new Person
{
    Name = "Lisa Malfrey",
    DateOfBirth = new DateTime(1983, 10, 11),
    SiblingCount = 1
};

await lisa.SaveAsync();

Console.WriteLine($"Lisa's ID: {lisa.ID}");
Console.Read();
Enter fullscreen mode Exit fullscreen mode

now run the program by hitting ctrl+f5. you will see that the entity was saved and automatically assigned an ID.

note: the ID property comes from the base Entity class so that we don't have to keep adding it manually to each entity we create.

if you look inside the database using a db manager or mongo shell, you'll see that a collection called Person was created and a new record was added which looks like this:

{
    "_id": ObjectId("5e0c682ddd3765736cb8ca56"),
    "Name": "Lisa Malfrey",
    "DateOfBirth": ISODate("1983-10-10T18:30:00Z"),
    "SiblingCount": 1
}
Enter fullscreen mode Exit fullscreen mode

Retrieving Entities

data can be retrieved in a couple of different ways. here are a few examples:

find by ID

var result = await DB.Find<Person>().OneAsync(lisa.ID);

Console.WriteLine($"Found Person: {result.Name}");
Console.Read();
Enter fullscreen mode Exit fullscreen mode

find by sibling count

var result = (await DB.Find<Person>()
                      .ManyAsync(p => p.SiblingCount >= 1))
                      .First();

Console.WriteLine($"Count: {result.SiblingCount}");
Console.Read();
Enter fullscreen mode Exit fullscreen mode

here we're saying find many Person entities who has at least 1 sibling. the .First() linq method simply picks the first person from the found list.

find by date range

var result = await DB.Queryable<Person>()
                     .Where(p => p.DateOfBirth > new DateTime(1983, 10, 10) &&
                                 p.DateOfBirth < new DateTime(1983, 10, 12))
                     .FirstOrDefaultAsync();

Console.WriteLine($"Birthday: {result.DateOfBirth.ToLocalTime()}");
Console.Read();
Enter fullscreen mode Exit fullscreen mode

here we're using the IQueryable interface to retrieve the first person who has a date of birth that falls within the range of two dates.

Updating Entities

you can either retrieve the complete entity, update it's properties and save it back to the database or update certain properties of entities without retrieving them first.

update by retrieving complete entity

var person = await DB.Find<Person>().OneAsync(lisa.ID);

person.Name = "Lisa Kudrow";
person.SiblingCount = 2;

await person.SaveAsync();
Enter fullscreen mode Exit fullscreen mode

update properties without retrieving

await DB.Update<Person>()
        .Match(p => p.ID == lisa.ID)
        .Modify(p => p.Name, "Lisa Kudrow")
        .Modify(p => p.SiblingCount, 2)
        .ExecuteAsync();
Enter fullscreen mode Exit fullscreen mode

Next Steps...

hope the above code has piqued your interest enough to go deeper into learning how to use mongodb with c# the easy way. the package MongoDB.Entities makes it extremely easy to communicate with mongodb server. every aspect of it's api has been documented on the official website. you can also check out the source code on github:

GitHub logo dj-nitehawk / MongoDB.Entities

A data access library for MongoDB with an elegant api, LINQ support and built-in entity relationship management

license nuget nuget tests discord

MongoDB.Entities

A light-weight .net standard library with barely any overhead that aims to simplify access to mongodb by abstracting the official driver while adding useful features on top of it resulting in an elegant API surface which produces beautiful, human friendly data access code.

More Info:

please visit the official website for detailed documentation:

https://mongodb-entities.com

Top comments (20)

Collapse
 
anthopark profile image
Anthony Park • Edited

Thank you so much for this beautiful wrapper library. I'm about to use this library in my next project and was wondering how the DI scoping works with the DB connection.

I'm used to the idea of having a DBContext that is scoped lifecycle and use that DBContext in a repository object, which I also register in a DI container as the scoped.

Would you recommend making a repository class that uses DB? If so, how does the lifecycle works?

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

you're most welcome. i believe this bit of info from the documentation answers your question. specifically, have a look at the repository pattern sample project link.

Collapse
 
anthopark profile image
Anthony Park • Edited

This tremendously helped! I'm now thinking of testing a service object that depends on the repository. I'm thinking of try DB.InitAsync with mongo2go in the xunit project and was wondering if you have some suggestions around the right way to mocking the DB connection with your library.. Thanks a ton!

Thread Thread
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

integration testing is the recommended approach. something like this if it's a web api project.

however I think doing db.initasync with mongo2go address in the setup/class fixture should do the trick.

let me know if it doesn't and I'll have a deeper look.

Thread Thread
 
anthopark profile image
Anthony Park

Thanks for the quick response! I was able to connect to Mongo2Go with your library but came across an issue which I've described in your repo issues.

I think I may need to use Xunit's class fixture and persist the connection throughout the test cases in a test class.

Collapse
 
catalinradoi profile image
CatalinRadoi

Hi. Shouldn't "new DB()" be assigned to an object or something?

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

not necessary if you're only dealing with a single database. if dealing with multiple dbs, then you need to assign to a variable as shown here.

Collapse
 
catalinradoi profile image
CatalinRadoi

Thanks! One more question :P I'm a MongoDB Newbie

I have an entity called Zone, with only one property: public int SqlId { get;set; }

And I have an entity Search:
public class Search : Entity {
public Many Zones { get; set; }
public Search() => this.InitOneToMany(() => this.Zones);
}

this is how an add a new Search:

var search = new Search();
search.Save();
var zone1 = new Zone { SqlId = 1};
zone1.Save();

search.Zones.Add(zone1);

var zone2 = new Zone { SqlId = 2};
zone2.Save();
search.Zones.Add(zone2);

search.Save();

I have multiple issues:

  1. Zones are duplicated in MongoDb. A new document is created for each new SqlId.

I wanted to use the Id from Mongo, but I can't "overwrite" that one. Then I need to make this SqlId unique or something.

  1. There seem to be a lot of "saves" is this good (performance-wise)?
Thread Thread
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

you can do bulk saves like this:

            var zones = new[] {
                new Zone { SqlId = 1 },
                new Zone { SqlId = 2 }
            };

            DB.Save(zones);

            var search = new Search();
            search.Save();

            search.Zones.Add(zones);

Zones are duplicated in MongoDb. A new document is created for each new SqlId.

i don't really understand your requirement here. do you not want to create two new zone documents?

Thread Thread
 
catalinradoi profile image
CatalinRadoi

Thanks for answering, you are awesome :)

no, I don't want to create two new zones.
Zones are like a META table or "Dictionary".

I only have 20 zones, from id = 1 to id = 20

A search could have a combination of these zones.
1, 2
or 1,2,3

I want to create two new Search documents, but I don't want the Zones to be created each time.

Thread Thread
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

for seeding the zone entities once, you can use the migrations feature by first writing a migration class like this:

public class _001_seed_zone_data : IMigration
{
    public void Upgrade()
    {
        var zones = new List<Zone>();

        for (int i = 1; i <= 20; i++)
        {
            zones.Add(new Zone { SqlId = i });
        }

        zones.Save();
    }
}

then on your application startup simply call:

    DB.Migrate();

then you can create your searches like this:

    var wantedZones = new[] { 1, 2, 3 };

    var foundZones = DB.Find<Zone>()
                       .Many(z => wantedZones.Contains(z.SqlId));

    var seach1Zones = foundZones.Where(z => new[] { 1, 2 }.Contains(z.SqlId));
    var search2Zones = foundZones.Where(z => new[] { 1, 2, 3 }.Contains(z.SqlId));

    var search1 = new Search();
    var search2 = new Search();

    DB.Save(new[] { search1, search2 });

    search1.Zones.Add(seach1Zones);
    search2.Zones.Add(search2Zones);
Thread Thread
 
catalinradoi profile image
CatalinRadoi

Thanks, you are very cool. I will try it now

Thread Thread
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

here's my test program in case you need to have a look: gist.github.com/dj-nitehawk/8bde0d...

Thread Thread
 
catalinradoi profile image
CatalinRadoi

Thank you :) I bought you a coffee for your outstanding help.

This project has a lot of potentials! I will recommend it to fellow C# devs.

Thread Thread
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

you are most welcome. just received the email notification regarding the donation. much appreciated. if you ever need any more help, just go to the github repo and open up a new issue. i'll be more than glad to help. have a great day!

Collapse
 
reachej profile image
Erik • Edited

I'm trying to use MongoDB.Entities in Uipath Studio via the Invoke code activity. I was unable to get the connection initialized until I did this (the instructions are wrong on the website):

string connectionstring = "mongodb+srv://m220student:m220password@mflix.XXXXX.mongodb.net/mflix?retryWrites=true&w=majority";
Console.WriteLine("connection string :"+ connectionstring);
string databasename = "mflix";
DB.InitAsync(databasename,MongoClientSettings.FromConnectionString(connectionstring));
Console.WriteLine("initialized");

Now I'm trying to figure out how to select a database and create a document in a collection. In the invoke code activity in uipath, if you have the word await it won't compile and if you declare a class it won't compile.

Collapse
 
feddessi profile image
Federico Dessì • Edited

Hi, I've read through your documentation but I can't find what I'm looking for. In the official MongoDB driver you can specify if you want your ID to be a specific field instead of the generic ObjectID, how can I do the same here?

I'll be more specific: In your Wiki you give the example of a Book entity, I would like to specify the _id field to be the ISBN number (for example) instead of auto generating a mongo ObjectID. Is this possible?

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

with this library it's not possible to change the type of the default id as objectid was chosen to be the best option for >90% of cases. you can however simply add another property to store the ISBN and put an index on it so queries can be written on that instead of querying by the ID. imho it's a small price to pay considering the benefits offered by the library.

Collapse
 
adie305 profile image
Adie305

Hi. How to make C# Windows Form Login using mongodb?

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

sorry mate cannot help you there as I haven't worked with winforms in over 10yrs. but the data access logic is the same. just use the DB static class to store and retrieve your entities as explained in the tutorial.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.