A guide to bulk write operations in MongoDB with C#
Tags: mongodb, dotnet, csharp, showdev
Published: 2020-01-22 14:58:25

***
*Welcome!* Spanish articles on [LinkedIn](https://www.linkedin.com/in/mpetrinidev/). You can follow me on [Twitter](https://twitter.com/mpetrinidev) for news.
***

## Table of Contents
1. [MongoDB and C# intro] (#mongodb-and-c-introduction)
2. [Insert operations](#insert-operations)
3. [Update operations](#update-operations)
4. [Delete operations](#delete-operations)
5. [Put it all together](#put-it-all-together)

# MongoDB and C# introduction

🚀 MongoDB provides the ability to perform bulk insert, update, and delete operations.

In the [MongoDB C# Driver,](http://mongodb.github.io/mongo-csharp-driver/2.10/getting_started/quick_tour/), we can use the `BulkWriteAsync()` method that supports the following write operations:

- InsertOne
- UpdateOne
- UpdateMany
- DeleteOne
- DeleteMany

We are going to show how to use these methods with the MongoDB's C# driver.

I'll work through this sample in MongoDB Atlas, the global cloud database service offered by MongoDB.

You can create your cluster in minutes, completely free [here](https://www.mongodb.com/cloud/atlas)

# Let's begin

I create a user collection with the following fields.

- _id
- name
- email
- createdAt
- isBlocked

YES! There is no *password*! It's not the scope of this post.

```csharp
    public class User
    {
        [BsonId] public ObjectId _id { get; set; }
        public BsonString name { get; set; }
        public BsonString email { get; set; }
        public BsonDateTime createdAt { get; set; }
        public BsonBoolean isBlocked { get; set; }
    }
```

# Insert operations

## BulkWriteAsync method

This method is responsible for executing the bulk operations. `BulkWriteAsync` takes a variable number (list) of `WriteModel` instances.

We set our User model as a generic type parameter to the `WriteModel` class.

```csharp
    var listWrites = new List<WriteModel<User>>();
```

## Creating a fake user dataset

Then, we are going to create a fake dataset of 1000 new users and add this to the list of WriteModel type.

```csharp
    var totalNewUsers = 1000;
    
    for (int i = 0; i < totalNewUsers; i++)
    {
        var newUser = new User
        {
            name = $"customName-{i}",
            email = $"customEmail-{i}@domain{i}.com",
            createdAt = DateTime.Now,
            isBlocked = false
        };
        
        listWrites.Add(new InsertOneModel<User>(newUser));
    }
```

**Pay attention** to the `InsertOneModel` object. We are telling MongoDB that it's an insert operation. 

## Execute batch insert operation

Finally, we get the user collection and execute the bulk insert.

```csharp
    var userCollection = db.GetCollection<User>("users");
    var resultWrites = await userCollection.BulkWriteAsync(listWrites);

    Console.WriteLine($"OK?: {resultWrites.IsAcknowledged} - Inserted Count: {resultWrites.InsertedCount}");
```

By default, MongoDB executes the bulk insert with an ordered approach. That's means perform the operations serially. If an error occurs of one of the insert operation, MongoDB **ends** the bulk insert. If you want MongoDB to continue with the bulk insert, you need to specify the unordered approach passing a `BulkWriteOptions` object:

```csharp
    var resultWrites = await userCollection.BulkWriteAsync(listWrites, new BulkWriteOptions
    {
        IsOrdered = false
    });
```

## Complete Code

```csharp
    public static async Task BulkInsertMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        
        var listWrites = new List<WriteModel<User>>();
        var totalNewUsers = 1000;
        
        for (int i = 0; i < totalNewUsers; i++)
        {
            var newUser = new User
            {
                name = $"customName-{i}",
                email = $"customEmail-{i}@domain{i}.com",
                createdAt = DateTime.Now,
                isBlocked = false
            };
            
            listWrites.Add(new InsertOneModel<User>(newUser));
        }
        
        var userCollection = db.GetCollection<User>("users");
        var resultWrites = await userCollection.BulkWriteAsync(listWrites);

        Console.WriteLine($"OK?: {resultWrites.IsAcknowledged} - Inserted Count: {resultWrites.InsertedCount}");
    }
    
```

# Update operations

After insert 1,000 new users, we are going to update some of them!

## UpdateOne

This class (UpdateOneModel<T>) provides us a way to update only one document that matches a specific condition.

We need to set up a filter definition (condition) and the update definition (what fields are going to update)

```csharp
    var filterDefinition = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
    var updateDefinition = Builders<User>.Update.Set(p => p.isBlocked, true);
```

Then, add these definitions to our `UpdateOneModel` instance.

```csharp
    listWrites.Add(new UpdateOneModel<User>(filterDefinition, updateDefinition));

```

### Complete code for UpdateOne

```csharp
    public static async Task BulkUpdateOneMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        var userCollection = db.GetCollection<User>("users");

        var listWrites = new List<WriteModel<User>>();

        var filterDefinition = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
        var updateDefinition = Builders<User>.Update.Set(p => p.isBlocked, true);
        
        listWrites.Add(new UpdateOneModel<User>(filterDefinition, updateDefinition));

        await userCollection.BulkWriteAsync(listWrites);
    }
```

## UpdateMany

This class (UpdateManyModel<T>) allows us to update multiple documents that match a specific condition.

As we did with UpdateOne, we need to set up a filter definition and the updated definition.


```csharp
    var filterDefinition = Builders<User>.Filter.Eq(p => p.isBlocked, false);
    var updateDefinition = Builders<User>.Update.Set(p => p.isBlocked, true);
```

In this case, we are going to block all users that there aren't blocked yet.

### Complete code for UpdateMany

```csharp
    public static async Task BulkUpdateManyMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        var userCollection = db.GetCollection<User>("users");

        var listWrites = new List<WriteModel<User>>();

        var filterDefinition = Builders<User>.Filter.Eq(p => p.isBlocked, false);
        var updateDefinition = Builders<User>.Update.Set(p => p.isBlocked, true);
        
        listWrites.Add(new UpdateManyModel<User>(filterDefinition, updateDefinition));

        await userCollection.BulkWriteAsync(listWrites);
    }
```


## UpdateOne and UpdateMany

As we saw, the implementations of these methods are the same. The main difference is that if the condition matches multiple documents, UpdateOne will update the first matching document only while UpdateMany updates all documents.


# Delete operations

It's very similar to update operations, but in this case, we are going to delete some documents in our user collection.

## DeleteOne

This class (DeleteOneModel<T>) provides us a way to delete only one document that matches a specific condition

We need to set up a filter definition (condition)

```csharp
    var filterDefinition = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
```

In this case, we are going to delete the first document that matches an email equals customEmail-0@domain0.com.

### Complete code for DeleteOne

```csharp
    public static async Task BulkDeleteOneMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        var userCollection = db.GetCollection<User>("users");

        var listWrites = new List<WriteModel<User>>();

        var filterDefinition = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
        listWrites.Add(new DeleteOneModel<User>(filterDefinition));

        await userCollection.BulkWriteAsync(listWrites);
    }
```

## DeleteMany

This class (DeleteManyModel<T>) allows us to delete multiple documents that match a specific condition.

As we did with DeleteOne, we need to set up a filter definition.

```csharp
    var filterDefinition = Builders<User>.Filter.Eq(p => p.isBlocked, true);
```

In this case, we are going to delete all users that there are blocked.

### Complete code for DeleteMany

```csharp
    public static async Task BulkDeleteManyMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        var userCollection = db.GetCollection<User>("users");

        var listWrites = new List<WriteModel<User>>();

        var filterDefinition = Builders<User>.Filter.Eq(p => p.isBlocked, true);
        listWrites.Add(new DeleteManyModel<User>(filterDefinition));

        await userCollection.BulkWriteAsync(listWrites);
    }
```

## DeleteOne and DeleteMany

As we said with UpdateOne and UpdateMany, the implementations of these methods are the same. The main difference is that if the condition matches multiple documents, DeleteOne will delete the first matching document only while DeleteMany deletes all documents.

# Put it all together

MongoDB allows us to execute all of this together 🤩
The following sample does not make sense, but I want to show that it is possible!

We are going to group all previous operations in the same bulk write operations so that we will create 1,000 new users, then execute UpdateOne, UpdateMany, DeleteOne, and DeleteMany operations in the same bulk in order.

```csharp
    public static async Task BulkAllTogetherMongoDb()
    {
        IMongoDatabase db = _client.GetDatabase("sample_blog");
        
        var listWrites = new List<WriteModel<User>>();
        var totalNewUsers = 1000;
        
        //InsertOne
        for (int i = 0; i < totalNewUsers; i++)
        {
            var newUser = new User
            {
                name = $"customName-{i}",
                email = $"customEmail-{i}@domain{i}.com",
                createdAt = DateTime.Now,
                isBlocked = false
            };
            
            listWrites.Add(new InsertOneModel<User>(newUser));
        }
        
        //UpdateOne
        var filterDefinition = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
        var updateDefinition = Builders<User>.Update.Set(p => p.isBlocked, true);
        
        listWrites.Add(new UpdateOneModel<User>(filterDefinition, updateDefinition));
        
        //UpdateMany
        var filterDefinitionUpdateMany = Builders<User>.Filter.Eq(p => p.isBlocked, false);
        var updateDefinitionUpdateMany = Builders<User>.Update.Set(p => p.isBlocked, true);

        listWrites.Add(new UpdateManyModel<User>(filterDefinitionUpdateMany, updateDefinitionUpdateMany));
        
        //DeleteOne
        var filterDefinitionDeleteOne = Builders<User>.Filter.Eq(p => p.email, "customEmail-0@domain0.com");
        listWrites.Add(new DeleteOneModel<User>(filterDefinitionDeleteOne));
        
        //DeleteMany
        var filterDefinitionDeleteMany = Builders<User>.Filter.Eq(p => p.isBlocked, true);
        listWrites.Add(new DeleteManyModel<User>(filterDefinitionDeleteMany));
        
        var userCollection = db.GetCollection<User>("users");
        var resultWrites = await userCollection.BulkWriteAsync(listWrites);

        Console.WriteLine($"OK?: {resultWrites.IsAcknowledged} - Inserted Count: {resultWrites.InsertedCount}");
        Console.WriteLine($"Updated Count: {resultWrites.ModifiedCount}");
        Console.WriteLine($"Deleted Count: {resultWrites.DeletedCount}");
    }
```

Console output:

> OK?: True - Inserted Count: 1000
> Updated Count: 1000
> Deleted Count: 1000


I hope you enjoyed the post!