DEV Community

Cover image for Getting Started with MongoDB in EF Core
Anton Martyniuk
Anton Martyniuk

Posted on • Originally published at antondevtips.com on

Getting Started with MongoDB in EF Core

Entity Framework Core (EF Core) is a popular ORM for .NET, typically used with relational databases like SQL Server or PostgreSQL.
However, with the increasing popularity of NoSQL databases like MongoDB, developers often need to integrate these technologies into their applications.
EF Core 8.0 introduces support for MongoDB, making it easier to work with document-oriented data in your .NET projects with your favourite ORM.

In this blog post, I will show you how to get started with MongoDB in EF Core 8.0.

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.

Getting Started With MongoDB In EF Core 8.0

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
Enter fullscreen mode Exit fullscreen mode

Step 2: Add MongoDB Provider and Connect to Database

To connect to MongoDB in EF Core 8.0, you need to add the official MongoDB provider package to your project:

dotnet add package MongoDB.EntityFrameworkCore
Enter fullscreen mode Exit fullscreen mode

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

{
  "ConnectionStrings": {
    "MongoDb": "mongodb://admin:admin@mongodb:27017"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create EF Core DbContext

First, let's create a Shipment entity:

public class Shipment
{
    public required ObjectId Id { get; set; }
    public required string Number { get; set; }
    public required string OrderId { get; set; }
    public required Address Address { get; set; }
    public required string Carrier { get; set; }
    public required string ReceiverEmail { get; set; }
    public required ShipmentStatus Status { get; set; }
    public required List<ShipmentItem> Items { get; set; } = [];
    public required DateTime CreatedAt { get; set; }
    public required DateTime? UpdatedAt { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Here a ObjectId represents a document identifier in a MongoDb collection.

When working with MongoDB, you can create a familiar EF Core DbContext, the same way you do when working with SQL databases:

public class EfCoreMongoDbContext(DbContextOptions options) : DbContext(options)
{
    public DbSet<Shipment> Shipments { get; init; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Shipment>()
            .ToCollection("shipments")
            .Property(x => x.Status).HasConversion<string>();
    }
}
Enter fullscreen mode Exit fullscreen mode

You need to add all the entities to the model builder and specify the collection names from the MongoDB.
And now you can use the DbSet in the same manner as with an SQL table.

Notice, that you can use Conversion for your entities.
In the code above we use string Conversion to make sure that enums are stored as strings in the database.

Finally, we need to register our DbContext and specify UseMongoDB for it:

var mongoConnectionString = configuration.GetConnectionString("MongoDb")!;

builder.Services.AddDbContext<EfCoreMongoDbContext>(x => x
    .EnableSensitiveDataLogging()
    .UseMongoDB(mongoConnectionString, "shipping")
);
Enter fullscreen mode Exit fullscreen mode

MongoDB is a NoSQL database that doesn't have a strict schema definition like SQL does, so you don't need to create any migrations.

Now we are ready to execute our first queries in the MongoDB.

Writing Data Into MongoDb with EF Core

Writing data into Mongodb with EF Core doesn't differ from writing data into SQL Server or PostgreSQL.
The real benefit of using EF Core with MongoDB is that you don't know that you are working with a NoSQL database under the hood.

This is beneficial as you can migrate from a SQL database to MongoDB with only a few tweaks if you're using EF Core.
Or get started with MongoDB right away without having to learn a new API.

But be aware that not all MongoDB features are available in EF Core.
You may need to use the MongoDB.Driver directly for some advanced features.

Here is how you can create, update and delete a document in MongoDB with EF Core:

// Create new shipment
var shipment = request.MapToShipment(shipmentNumber);
context.Shipments.Add(shipment);
await context.SaveChangesAsync(cancellationToken);

// Update shipment
shipment.Status = ShipmentStatus.Delivered;
await context.SaveChangesAsync(cancellationToken);

// Delete shipment
context.Shipments.Remove(shipment);
await context.SaveChangesAsync(cancellationToken);
Enter fullscreen mode Exit fullscreen mode

When assigning an Id, you can use the ObjectId factory method:

Id = ObjectId.GenerateNewId();
Enter fullscreen mode Exit fullscreen mode

Reading Data From MongoDb with EF Core

As you can guess, you can use the familiar LINQ methods in EF Core to read data from MongoDB.

Here is how to select single or multiple records:

var shipment = await context.Shipments
    .Where(x => x.Number == request.ShipmentNumber)
    .FirstOrDefaultAsync(cancellationToken: cancellationToken);

var shipments = await context.Shipments
    .Where(x => x.Status == ShipmentStatus.Dispatched)
    .ToListAsync(cancellationToken: cancellationToken);
Enter fullscreen mode Exit fullscreen mode

You can also provide a filtering predicate inside a FirstOrDefaultAsync or other similar methods.

Here is how to check if any entity exists or get count of entities:

var shipmentAlreadyExists = await context.Shipments
    .Where(s => s.OrderId == request.OrderId)
    .AnyAsync(cancellationToken);

var count = await context.Shipments
    .CountAsync(x => x.Status == ShipmentStatus.Delivered);
Enter fullscreen mode Exit fullscreen mode

Limitations When Using Entity Framework 8.0 With MongoDB

The following features are not supported in EF 8.0 for MongoDB:

1. Select Projections:
Select projections use the Select() method in a LINQ query to change the structure of the created object.
EF 8 doesn't support such projections.

2. Scalar Aggregations:
EF 8 only supports the following scalar aggregation operations:

  • Count(), CountAsync()
  • LongCount(), LongCountAsync()
  • Any(), AnyAsync() with or without predicates.

3. Transactions:
EF 8 does not support the Entity Framework Core transaction model for MongoDB.

If you need any of these features without restrictions - use MongoDB.Driver directly.

MongoDB is a NoSQL database, it doesn't support the following EF Core features:

  • Migrations
  • Foreign and Alternate Keys
  • Table Splitting and Temporal Tables
  • Spatial Data

For more information, you can read the official MongoDB documentation.

Summary

EF Core 8 allows seamless integration with MongoDB with familiar Entity Framework classes, interfaces and methods.
You can use the DbContext to configure MongoDB collections.
You can use your favourite LINQ methods to perform read operations and the familiar methods for write operations.

For sure, in the next versions of Entity Framework, support for more and more features will be added for MongoDB.

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.

Top comments (6)

Collapse
 
fw_desperado profile image
Gregory Hampton

Great summary. I am especially interested in the modelbuilder options. I started a project and decided to try and use strongly typed IDs. I keep getting the following error whenever I try to access a record in a collection (a record which I manually entered):
{
"code": 13,
"message": "Alternate keys are not supported by the MongoDB EF Core Provider.",
"details": []
}
Any suggestion on how to configure an Id.Value to map to a Mongo _id?

Collapse
 
antonmartyniuk profile image
Anton Martyniuk

Try something like this:

builder.Property(x => x.Id)
    .HasConversion(
        id => id.Value,
        value => new OrderId(value)
    )
    .IsRequired();
Enter fullscreen mode Exit fullscreen mode
Collapse
 
fw_desperado profile image
Gregory Hampton

I tried the follow, but still get the same error:

modelBuilder.Entity<Institution>()
            .ToCollection("institutions")
            .Property(i => i.Id).HasConversion(id => id.Value, value => new InstitutionId(value))
            .IsRequired();
Enter fullscreen mode Exit fullscreen mode

Just for reference, my Institution Id is defined as:

  public record InstitutionId(Ulid Value);
Enter fullscreen mode Exit fullscreen mode

And the Institution object contains:

public InstitutionId Id { get; set; } = new InstitutionId(new Ulid());
Enter fullscreen mode Exit fullscreen mode

Thanks again for the help.

Thread Thread
 
antonmartyniuk profile image
Anton Martyniuk

@fw_desperado
strange, that it doesn't work.
Try this and let me know if it worked for you:
stackoverflow.com/a/78491149/3914385

Collapse
 
fw_desperado profile image
Gregory Hampton

One additional question, in reviewing my EF configuration and this blog post, I noticed that the database instance is not specified anywhere. If you are hosting multiple databases on the same Mongo DB instance, does EF/Mongo simply keep track based on the user credentials?

Collapse
 
antonmartyniuk profile image
Anton Martyniuk

@fw_desperado
the database name is specified in the UseMongoDB as 2nd parameter:

UseMongoDB(mongoConnectionString, "shipping")
Enter fullscreen mode Exit fullscreen mode

I always recommend specifying the database name in every application