Using MongoDB in Your ASP.NET Core App

ASP.NET Core apps are usually built with SQL Server or MySQL and the Entity Framework ORM in mind, but what if you would like to use MongoDB, a document store NoSQL database, with your app instead?

Add the MongoDB Driver

First, you must add the official MongoDB driver to your project via NuGet:

Install-Package MongoDB.Driver

This driver will also add annotations that you can use to configure your model definitions with.

Add a Model

Here is a basic model example:

public class Article
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
    public string Slug { get; set; }
    public string Body { get; set; }
    public DateTime Date { get; set; }
}

The [BsonId] annotation designates the property you use it with as the primary key. You can define that property as an ObjectId, which is how MongoDB stores primary keys, but defining it as a string makes it easier to work with in your code thanks to the automatic conversion handled by the [BsonRepresentation(BsonType.ObjectId)] annotation.

With the model defined, you can start interfacing with MongoDB by creating an instance of MongoClient somewhere in your code, such as directly within a service class or a custom context that you can create similar to the DbContext from Entity Framework. In this article, I show you the latter way.

Add a Custom Context

Here is an example DB context class:

public class AppDbContext
{
    private readonly IMongoDatabase _db;
    
    public AppDbContext(IMongoClient client, string dbName)
    {
        _db = client.GetDatabase(dbName);
    }
    
    public IMongoCollection<Article> Articles => _db.GetCollection<Article>("articles");
}

The constructor will allow us to inject an instance of MongoClient and the DB connection string for your database. This class contains properties for the collections of your models. _db.GetCollection<Article>("articles") will create or retrieve documents from the "articles" collection in your database and map the results to the model definition. You can rename the collection from "articles" to another if you wish. I prefer to keep it pluralized and lowercase to follow the common camel case naming convention. Later on, you should have this context class implement an interface so you can inject mock class instances for testing.

Add a DB connection string and a DbName property to your appsettings.json file with your MongoDB host and port like so:

{
    "ConnectionStrings": {
        "MongoDb": "mongodb://localhost:27017"
    },
    "DbName": "example"
}

Refer to "Connection String URI Format" in the MongoDB documentation if you would like to specify a username and password.

In your startup class, you should instantiate the context with the configuration settings you just added by adding these lines to the ConfigureServices method like so:

public void ConfigureServices(IServiceCollection services)
{
    ConventionRegistry.Register("Camel Case", new ConventionPack {new CamelCaseElementNameConvention()}, _ => true);
    services.AddSingleton<IMongoClient>(s => new MongoClient(Configuration.GetConnectionString("MongoDb")));
    services.AddScoped(s => new AppDbContext(s.GetRequiredService<IMongoClient>(), Configuration["DbName"]));
}

The MongoClient instance should be a singleton as recommended by the MongoDB documentation because it represents a pool of connections. AppDbContext should have a scoped lifetime so that it can be reused efficiently. I also added a line to register a convention to force model properties to save with camel case as it saves your properties with the case you defined them with by default. With the context dependency configured, you can now create service classes to handle data access and business logic for your collections.

Add a Service

Here is a simple service class example using asynchronous methods:

public class ArticleService
{
    private readonly AppDbContext _db;
    public ArticleService(AppDbContext db)
    {
        _db = db;
    }
    public async Task<Article> GetByIdAsync(string id)
    {
        return await _db.Articles.Find(a => a.Id == id).SingleOrDefaultAsync();
    }
    public async Task<Article> GetBySlugAsync(string slug)
    {
        return await _db.Articles.Find(a => a.Slug == slug).SingleOrDefaultAsync();
    }
    
    public async Task AddAsync(Article article)
    {
        await _db.Articles.InsertOneAsync(article);
    }
}

The InsertOneAsync method will set the ID of the new document in the object if the document was successfully created. Refer to the MongoDB documentation for more information on methods you can use.

Add a dependency to this service in your startup class:

services.AddTransient<ArticleService>();

Use the Service

Here is a brief example of how you can use the service in an API controller:

[Route("api/[controller]")]
[ApiController]
public class ArticlesController : ControllerBase
{
    private readonly ArticleService _articleService;
    public ArticlesController(ArticleService articleService)
    {
        _articleService = articleService;
    }
    
    [HttpGet("{id}")]
    public async Task<Article> GetAsync(string id)
    {
        return await _articleService.GetByIdAsync(id);
    }
    
    [HttpPost]
    public async Task<IActionResult> PostAsync([FromBody] Article article)
    {
        await _articleService.AddAsync(article);
        
        return article.Id != null ? (IActionResult) Ok(article.Id) : BadRequest();
    }
}

Summary

In this article, you learned how to connect and interface with MongoDB in your ASP.NET Core project. If you find this guide helpful or have any questions on this topic, please leave a comment below.

Comments

Vaibhav Sharma

Very good information. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*

*