Best Practices When Working With MongoDb in .NET

MongoDB is one of the most popular NoSQL databases, it allows building modern and scalable applications.

In this blog post, I will show you what are the best practices when working with MongoDB in .NET and C#.

On my website: antondevtips.com I share…


This content originally appeared on DEV Community and was authored by Anton Martyniuk

MongoDB is one of the most popular NoSQL databases, it allows building modern and scalable applications.

In this blog post, I will show you what are the best practices when working with MongoDB in .NET and C#.

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to become a better developer.
Download the source code for this blog post for free.

Get Started with MongoDB in ASP.NET Core

You need to follow these steps to Add MongoDB to your project.

Step 1: Set Up MongoDB

We will set up MongoDB in a docker container using docker-compose-yml:

services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=admin
    volumes:
      - ./docker_data/mongodb:/data/db
    ports:
      - "27017:27017"
    restart: always
    networks:
      - docker-web

networks:
  docker-web:
    driver: bridge

Step 2: Add MongoDB Provider and Connect to Database

To connect to MongoDB, you need to add the official MongoDB package to your project:

dotnet add package MongoDB.Driver

Next you need to configure a connection string to the MongoDB in appsettings.json:

{
  "ConnectionStrings": {
    "MongoDb": "mongodb://admin:admin@mongodb:27017"
  }
}

Step 3: Register MongoDB Dependencies in DI Container

You need to register a IMongoClient as a single instance in the DI container:

var mongoConnectionString = configuration.GetConnectionString("MongoDb");
var mongoClientSettings = MongoClientSettings.FromConnectionString(mongoConnectionString);

services.AddSingleton<IMongoClient>(new MongoClient(mongoClientSettings));

This class is used to create a connection with a MongoDB database and allow performing database commands.

Now, as we're ready to go, let's explore a real-world application that uses MongoDB.

An Example Application

Let's explore a Shipping Application that is responsible for creating and updating customers, orders and shipments for ordered products.

This application has the following entities:

  • Customers
  • Orders, OrderItems
  • Shipments, ShipmentItems

Let's explore a Shipment and ShipmentItem entities:

public class Shipment
{
    public Guid Id { get; set; }
    public required string Number { get; set; }
    public required string OrderId { get; set; }
    public required Address Address { get; set; }
    public required ShipmentStatus Status { get; set; }
    public required List<ShipmentItem> Items { get; set; } = [];
}

public class ShipmentItem
{
    public required string Product { get; set; }
    public required int Quantity { get; set; }
}

At the end of the blog post, you can download a source code of Shippping Application.

How To Work With IDs in MongoDB

There are multiple options to create entity IDs in MongoDB:

  • using ObjectId MongoDB class
  • using string type with attributes
  • using Guid type without attributes

Let's explore all the options in code:

public class Shipment
{
    public ObjectId Id { get; set; }
}

public class Shipment
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
}

public class Shipment
{
    public Guid Id { get; set; }
}

The first option has the following disadvantages:

  • your entity is now aware of MongoDB
  • you need to manually create an ObjectId when inserting an entity

The second option allows MongoDB to automatically create IDs when inserting a record in the database.
But it also makes your entity aware of MongoDB.

The third option is the most common option for C# developers, which is also widely used when working with SQL databases.
This approach makes your entity separated from MongoDB.

Among these options, I prefer using the 3rd option with Guid.
In some cases, I can use the 2nd option with string and attributes as well.

To be able to work with Guid you need to turn this feature on:

BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;
BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));

Never mind that BsonDefaults.GuidRepresentationMode is obsolete.
In a future version it will be removed from the API and GuidRepresentation.Standard will turn on the GuidRepresentationMode.V3 by default.

How To Configure Properties Serialization in MongoDB

MongoDB stores all the data in the database in the BSON format (binary JSON).
The default casing in JSON is camelCasing, which I recommend to turn on for serialization of C# classes to MongoDB collections.

You need to register CamelCaseElementNameConvention in the MongoDB ConventionRegistry:

ConventionRegistry.Register("camelCase", new ConventionPack {
    new CamelCaseElementNameConvention()
}, _ => true);

When working with Enum values, I recommend serializing it as string in the database.
It makes your collection data much more expressive and readable rather than having numbers, which is the default way of Enum serialization.

This is how you can register it in the ConventionRegistry:

ConventionRegistry.Register("EnumStringConvention", new ConventionPack
{
    new EnumRepresentationConvention(BsonType.String)
}, _ => true);

Screenshot_1

In our application, a Shipment has an enum value ShipmentStatus:

public class Shipment
{
    public Guid Id { get; set; }
    public required string Number { get; set; }
    public required string OrderId { get; set; }
    public required Address Address { get; set; }
    public required ShipmentStatus Status { get; set; }
    public required List<ShipmentItem> Items { get; set; } = [];
}

public enum ShipmentStatus
{
    Created,
    Processing,
    Dispatched,
    InTransit,
    Delivered,
    Received,
    Cancelled
}

The Best Way To Work With Collections in MongoDB

And here is the most interesting part: I will show you what I think is the best way to work with MongoDB collections in C# code.

Every time you need to perform a database command you need to extract a database from IMongoClient.
Then you need to extract a collection from the database.

var database = mongoClient.GetDatabase("shipping-api");
var collection = database.GetCollection<Shipment>("shipments");

Every time you need to path a database and a collection name.
This is tedious and can be error-prone.

One way to solve this problem is by introduction of constants:

public static class MongoDbConsts
{
    public const string DatabaseName = "shipping-api";

    public const string ShipmentCollection = "shipments";
}

var database = mongoClient.GetDatabase(DatabaseName);
var collection = database.GetCollection<Shipment>(ShipmentCollection);

This approach is also error-prone - you can pass a wrong collection name, for example.

Here is my favourite approach for organizing code when working with MongoDB collections:

public class MongoDbContext(IMongoClient mongoClient)
{
    private readonly IMongoDatabase _database = mongoClient.GetDatabase("shipping-api");

    public IMongoCollection<Shipment> Shipments => _database.GetCollection<Shipment>("shipments");

    public IMongoCollection<Customer> Customers => _database.GetCollection<Customer>("customers");

    public IMongoCollection<Order> Orders => _database.GetCollection<Order>("orders");
}

I like creating a MongoDbContext class that encapsulates all IMongoCollections.
I find this approach useful as I can keep all the database and collection names in one place.
With this approach, I can't mess up with a wrong collection name.

To be able to inject MongoDbContext into your classes, simply register it as Singleton in DI:

services.AddSingleton<MongoDbContext>();

Here is how you can use MongoDbContext:

public async Task<ErrorOr<ShipmentResponse>> Handle(
    CreateShipmentCommand request,
    CancellationToken cancellationToken)
{
    var shipmentAlreadyExists = await mongoDbContext.Shipments
        .Find(x => x.OrderId == request.OrderId)
        .AnyAsync(cancellationToken);

    if (shipmentAlreadyExists)
    {
        logger.LogInformation("Shipment for order '{OrderId}' is already created", request.OrderId);
        return Error.Conflict($"Shipment for order '{request.OrderId}' is already created");
    }

    var shipmentNumber = new Faker().Commerce.Ean8();
    var shipment = request.MapToShipment(shipmentNumber);

    await mongoDbContext.Shipments.InsertOneAsync(shipment, cancellationToken: cancellationToken);

    logger.LogInformation("Created shipment: {@Shipment}", shipment);

    var response = shipment.MapToResponse();
    return response;
}

If you have experience with EF Core, it really looks familiar.

At the end of the blog post, you can download a source code of Shippping Application.

Summary

When working with MongoDB in .NET and C#, I recommend using the following best practices:

  • Use Guid or string for Id field
  • Use camelCase when serializing entities into a database
  • Serialize enums as strings
  • Create MongoDbContext to manage database collections in one place

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to become a better developer.
Download the source code for this blog post for free.


This content originally appeared on DEV Community and was authored by Anton Martyniuk


Print Share Comment Cite Upload Translate Updates
APA

Anton Martyniuk | Sciencx (2024-10-17T19:34:49+00:00) Best Practices When Working With MongoDb in .NET. Retrieved from https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/

MLA
" » Best Practices When Working With MongoDb in .NET." Anton Martyniuk | Sciencx - Thursday October 17, 2024, https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/
HARVARD
Anton Martyniuk | Sciencx Thursday October 17, 2024 » Best Practices When Working With MongoDb in .NET., viewed ,<https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/>
VANCOUVER
Anton Martyniuk | Sciencx - » Best Practices When Working With MongoDb in .NET. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/
CHICAGO
" » Best Practices When Working With MongoDb in .NET." Anton Martyniuk | Sciencx - Accessed . https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/
IEEE
" » Best Practices When Working With MongoDb in .NET." Anton Martyniuk | Sciencx [Online]. Available: https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/. [Accessed: ]
rf:citation
» Best Practices When Working With MongoDb in .NET | Anton Martyniuk | Sciencx | https://www.scien.cx/2024/10/17/best-practices-when-working-with-mongodb-in-net/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.