In this article, we will implement basic operations in a MongoDB database using .NET and C#. We will use the MongoDB.Driver package to perform various operations. This article focuses on MongoDB operations, so we will not implement a high-level architecture for the .NET project. Instead, we will create a .NET 8 Web API project and implement all operations directly in the controller rather than creating separate service or repository classes. You can implement those architectures based on your requirements.

In this article

MongoDB

What Is MongoDB?

MongoDB is a type of database that stores information in a way that's easy to read and use. It's flexible, meaning it can handle different types of data without needing a fixed structure. MongoDB works quickly and can manage a lot of information at once. It's good for storing data from websites, apps, and other digital sources because it's designed to handle large amounts of information and can be set up to work with multiple computers at the same time.

Configuring MongoDB in a .NET Application

To set up MongoDB for use in your .NET application, follow these steps.

Step 1. Install MongoDB.Driver Package

The first step is to install the MongoDB.Driver package. This package allows your .NET application to communicate with MongoDB. Here's how you can do it.

Manage NuGet Packages

Step 2. Configure MongoDB Connection Details

Next, you need to configure the MongoDB client to connect to your MongoDB server. You'll need to specify the server's URL and the database name you want to use. These configuration details are typically stored in your application's settings file (`appsettings.json`).

Here's an example of how to configure MongoDB settings in `appsettings.json`.

{
  "MongoDbSettings": {
    "ConnectionString": "mongodb://localhost:27017",  
    "DatabaseName": "MongoWithDotNet",                
    "Collections": {
      "EmployeeCollection": "Employees"                
    }
  }
}

In this configuration

Step 3. Register MongoDB Client in Your Application

Now, you need to register the MongoDB client in your .NET application. This allows your application to use MongoDB services throughout its components. Typically, this registration is done in Program.cs or Startup.cs file.

Here’s how you can register the MongoDB client using Dependency Injection (DI).

    // Register MongoDB client
var connectionString = builder.Configuration.GetValue<string>("MongoDbSettings:ConnectionString");
var settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
settings.SslSettings = new SslSettings() { EnabledSslProtocols = SslProtocols.Tls12 };
builder.Services.AddSingleton<IMongoClient>(new MongoClient(settings));

In this code

By following these steps, your .NET application is configured to connect and interact with MongoDB. You can now proceed to implement MongoDB operations such as inserting, querying, updating, and deleting data within your application.

Understanding MongoDB Database Operations

We can perform database operations within C# code. The MongoDB client allows us to perform the following database operations.

To understand these operations, we will create a new controller where we will perform all the above-mentioned operations. We will directly use the Mongo client in our controller without creating any service or repository.

To use these database operations, we first have to create an instance of the Mongo database object. We will create it in the constructor by reading the database name from the app settings file.

private readonly IMongoDatabase _database;
public MongoDatabaseController(IMongoClient mongoClient, IConfiguration configuration)
{
    var databaseName = configuration.GetValue<string>("MongoDbSettings:DatabaseName");
    _database = mongoClient.GetDatabase(databaseName);
}

Creating a Collection

To initiate a new collection within your MongoDB database, utilize the CreateCollectionAsync method provided by the IMongoDatabase interface. Below is an example demonstrating its implementation in a controller.

[HttpPost("create-collection/{name}")]
public async Task<IActionResult> CreateCollection(string name)
{
    try
    {
        await _database.CreateCollectionAsync(name);
        return Ok($"Collection '{name}' created successfully.");
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"Failed to create collection '{name}'. Error: {ex.Message}");
    }
}

Creating a collection explicitly allows you to define it in advance, although MongoDB typically creates collections automatically when you insert the first document. This approach proves beneficial for configuring collections with specific attributes like validation rules or predefined indexes. By defining collections explicitly, you can efficiently structure your database from the outset, enhancing organization and performance optimization.

Explicitly creating collections ensures that your MongoDB setup aligns with your application's requirements from the start. This proactive approach facilitates smoother data management and more efficient query handling as your application scales.

Dropping a Collection

To remove a collection and all its associated documents from your MongoDB database, utilize the DropCollectionAsync method provided by the IMongoDatabase interface. Below is an example implementation within a controller.

[HttpDelete("drop-collection/{name}")]
public async Task<IActionResult> DropCollection(string name)
{
    try
    {
        await _database.DropCollectionAsync(name);
        return Ok($"Collection '{name}' dropped successfully.");
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"Failed to drop collection '{name}'. Error: {ex.Message}");
    }
}

Dropping a collection is a potent operation typically employed for cleanup or resetting purposes during application development. It effectively deletes the specified collection along with all its documents from the database.

However, exercise caution when performing this operation to avoid unintended data loss. Double-check and accurately specify the collection name before proceeding with the drop operation.

This method ensures that your MongoDB database remains organized and streamlined, allowing for efficient data management and maintenance as your application evolves.

Listing Collections

To get a list of all collections in your database, the ListCollectionNamesAsync method is used. This method returns the names of all collections as a cursor, which you can then convert to a list.

[HttpGet("list-collections")]
public async Task<IActionResult> ListCollections()
{
    try
    {
        var cursor = await _database.ListCollectionNamesAsync();
        var collectionNames = await cursor.ToListAsync();
        return Ok(collectionNames);
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"Failed to retrieve collection names. Error: {ex.Message}");
    }
}

Listing collections is beneficial for dynamically inspecting the database structure, especially in applications that need to adapt to different collections. By retrieving the collection names, you can programmatically handle various collections, making your application more flexible and robust.

Renaming a Collection

Sometimes, you may need to rename an existing collection, which can be done using the RenameCollectionAsync method. This method changes the name of a collection from an old name to a new one.

[HttpPost("rename-collection/{oldName}/{newName}")]
public async Task<IActionResult> RenameCollection(string oldName, string newName)
{
    try
    {
        await _database.RenameCollectionAsync(oldName, newName);
        return Ok($"Collection '{oldName}' renamed to '{newName}' successfully.");
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"Failed to rename collection '{oldName}' to '{newName}'. Error: {ex.Message}");
    }
}

Renaming a collection keeps the existing data and indexes intact while changing the collection's name. This operation is useful for refactoring or reorganizing your database without losing any data. It's also handy when you want to follow new naming conventions or improve clarity in your database structure. Ensure the new name does not conflict with an existing collection to avoid issues.

Getting a Collection

To interact with a specific collection, you retrieve it using the GetCollection<T> method. This method returns a collection object that you can use for CRUD operations.

[HttpPost("get-collection/{collectionName}")]
public IActionResult GetCollection(string collectionName)
{
    var collection = _database.GetCollection<Employee>(collectionName);
    return Ok();
}

Retrieving a collection with GetCollection<T> allows you to perform various operations, such as inserting, updating, querying, and deleting documents. The method is generic, meaning you specify the type of documents the collection holds (e.g., Employee). This ensures type safety and enables IntelliSense support in your IDE, making your code easier to write and maintain.

Understanding MongoDB Collection Operations

we will explore fundamental actions such as,

I have developed a new C# class named Employee specifically for demonstrating these operations within a controller. This class defines the structure of an employee document, including fields such as ID, Name, Age, Designation, and Salary. These operations are implemented using MongoDB.Driver package in .NET facilitates efficient data management and manipulation within MongoDB collections.

public class Employee
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Designation { get; set; }
    public decimal Salary { get; set; }
} 

Initialize the MongoDB collection

public class MongoCollectionController : ControllerBase
{
    private readonly IMongoCollection<Employee> _employeeCollection;
    public MongoCollectionController(IMongoClient mongoClient, IConfiguration configuration)
    {
        var databaseName = configuration.GetValue<string>("MongoDbSettings:DatabaseName");
        var employeeCollectionName = configuration.GetValue<string>("MongoDbSettings:Collections:EmployeeCollection");
        var database = mongoClient.GetDatabase(databaseName);
        _employeeCollection = database.GetCollection<Employee>(employeeCollectionName);
    }
}

Explanation

The MongoCollectionController class initializes and manages interactions with a MongoDB collection named Employee. In the constructor.

By implementing these MongoDB collection operations in your .NET application, you can efficiently manage data stored in MongoDB databases. Each operation leverages the MongoDB.Driver package to integrate seamlessly with .NET applications, providing robust capabilities for CRUD operations and data manipulation.

Inserting a Document

    [HttpPost("create")]
    public async Task<IActionResult> Create()
    {
        try
        { 
            var employee = new Employee { Id = ObjectId.GenerateNewId().ToString(), Name = "John Doe", Age = 30 };             
            await _employeeCollection.InsertOneAsync(employee);
            return Ok();
        }
        catch (MongoWriteException ex)
        { 
            return BadRequest($"Failed to insert document: {ex.Message}");
        }
    }

Explanation

Note. MongoDB ensures the uniqueness of the _id field by default. If you attempt to insert a document with an ID that already exists in the collection, it will throw a MongoWriteException with a message indicating a duplicate key error. This behavior is crucial for maintaining data integrity and preventing unintended duplicates in your MongoDB collections.

Inserting Multiple Documents

[HttpPost("create-many")]
 public async Task<IActionResult> CreateMany()
 {
     var employees = new List<Employee>
     {
         new Employee { Id = ObjectId.GenerateNewId().ToString(), Name = "Test 1", Age = 25, Designation = "SSE", Salary = 15000 },
         new Employee { Id = ObjectId.GenerateNewId().ToString(), Name = "Test 2", Age = 35, Designation = "Team Leader", Salary = 105000 },
         new Employee { Id = ObjectId.GenerateNewId().ToString(), Name = "Test 3", Age = 35, Designation = "Test", Salary = 151022},
         new Employee { Id = ObjectId.GenerateNewId().ToString(), Name = "Test 4", Age = 35, Designation = "Demo", Salary = 1500}
     };
     await _employeeCollection.InsertManyAsync(employees);
     return Ok();
 }

Explanation

Benefits of InsertMany

Get All Documents

Retrieve all documents from a collection using Find.

  [HttpGet("all")]
public async Task<IActionResult> GetAll()
{
    var employees = await _employeeCollection.Find(Builders<Employee>.Filter.Empty).ToListAsync();
    return Ok(employees);
}

Explanation

Get Document by Id

Retrieve a specific document by its ID using Find with a filter.

  [HttpGet("get-by-id/{id}")]
public async Task<IActionResult> GetById([FromRoute] string id)
{
    var employee = await _employeeCollection
                        .Find(Builders<Employee>.Filter.Eq(p => p.Id, id))
                        .FirstOrDefaultAsync();
    return Ok(employee);
}

Explanation

Replace Whole Document

Replace an entire document using ReplaceOne.

[HttpPut("replace/{id}")]
public async Task<IActionResult> Replace([FromRoute] string id)
{
    var filter = Builders<Employee>.Filter.Eq(p => p.Id, id);
    var employee = new Employee { Id = id, Name = "Updated Name", Age = 40 };
    var result = await _employeeCollection.ReplaceOneAsync(filter, employee);
    return Ok(result);
}

Explanation

Update Particular Field in the Document

Update specific fields in a document using UpdateOne.

   [HttpPatch("update-field/{id}")]
public async Task<IActionResult> UpdateField([FromRoute] string id)
{
    var filter = Builders<Employee>.Filter.Eq(p => p.Id, id);
    var update = Builders<Employee>.Update.Set(p => p.Name, "Partially Updated Name");
    var result = await _employeeCollection.UpdateOneAsync(filter, update);
    return Ok(result);
}

Explanation

Delete Document

Delete a document from the collection using DeleteOne.

  [HttpDelete("delete/{id}")]
public async Task<IActionResult> Delete([FromRoute] string id)
{
    var filter = Builders<Employee>.Filter.Eq(p => p.Id, id);
    var result = await _employeeCollection.DeleteOneAsync(filter);
    //var result = _employeeCollection.DeleteManyAsync(filter);
    return Ok(result);
}

Explanation

Get the Total Number of Documents in the Collection

Get the count of documents

  [HttpGet("count")]
public async Task<IActionResult> CountDocuments()
{ 
    var count = await _employeeCollection.CountDocumentsAsync(Builders<Employee>.Filter.Empty);
    return Ok(count);
}

Explanation

Estimated Count

   [HttpGet("count/estimated")]
public async Task<IActionResult> EstimatedDocumentCount()
{
    var count = await _employeeCollection.EstimatedDocumentCountAsync();
    return Ok(count);
}

Explanation

Conclusion

By implementing MongoDB operations in a .NET application, you gain efficient management of data stored in MongoDB databases. These operations leverage MongoDB.Driver to seamlessly integrate with .NET, providing robust capabilities for data manipulation and application development.

You can access the source code of this project from my GitHub.