MongoDB is a kind of NoSQL database. NoSQL is a document-oriented database that is organized as a JSON. Some points about MongoDB:
- Performance: It stores a majority of data in RAM, so the query performance is much better here than in a relational database. But it requires more RAM and precise indexes.
- Simplicity: Some users consider the query syntax is simpler here than in relational databases. The installation, configuration, and execution are simple and quick to do. And the learning curve is shorter than in the others.
- Flexibility: It’s dynamic because it doesn’t have a predefined schema.
- Scalability: It uses shards for horizontal scalability, which makes it easier to increase storage capacity.
- Transaction: v3.6 and beyond allow using the transaction concept. And v4.0 and beyond allow using the multi-document transaction concept.
Now that you know a little more about MongoDB, let’s go to our goal! This article proposes to teach you how you can build a .NET Core API connecting with MongoDB and use the Transactions with Dependency Injection.
For this example, don’t consider the structure and the architecture, it was built in this way just because I think that it’s easier to explain. So, I won’t explain the layers, folders, and other things in detail, except those needed by the use of MongoDB and the transactions.
In this case, I used the Visual Studio 2019, Community version. So, with the VS installed, we select the “ASP.NET Core Web Application” option and select the API type.
About the base structure
After creating the project, we create five folders like below:
- Controllers: responsible to answer the requests
- Entities: Domain classes
- Interfaces: like contracts, we’ll use it to do the DI and IoC
- Models: Classes that we’ll use to receive and return data on controllers
- Repositories: Classes with methods that contain the implementation of MongoDB operations
We’ll focus on just Controllers and Repositories folders and the Startup class. If you want to see the complete code, wait for the end of this article.
Installing and configuring MongoDB
Now, we need to install the more important package for our project, that is the MongoDB.Driver.
After installed, we need to modify the Startup class, on the ConfigureServices method, specifically.
Let’s analyze this code:
The ConfigureServices method is where we do our IoC. So, the first statement is where we make the MongoDB connection. Pay attention in this step because we make a Singleton, we do this because the MongoClient instance, in MongoDB, is already a pool connection, so if you don’t use a Singleton, a new pool connection will always be created.
And the second statement is where we declare the IoC, which we’ll use to start a session of the MongoDB Transaction. Notice that, in this case, we make a Scoped, because the transaction life cycle will be equal to the request life cycle.
Creating a base repository
So now, let’s make a base repository that we will use whenever we want to do some operations. First, the base repository class will receive a Generics <T> to identify the entity in our code that represents a collection in the database, like below:
After creating the class, let’s go to declare some attributes that will be important to us:
We have four attributes:
- DATABASE: it’s the Constant that represents the name of our database.
- _mongoClient: it’s the client interface to MongoDB. Using it we do some operations on the database.
- _clientSessionHandle: it’s the interface handle for a client session. So, if you start a transaction, you should pass the handle when you do some operation.
- _collection: it’s the name of the collection used by the Generic class.
All these attributes will receive a value on a class constructor:
Look at the constructor parameters. We receive these parameters by dependency injection. We get the values of parameters and we assign them to the attributes that we created.
Another very important thing is the code below of the assignments. If you work with transactions, on MongoDB, and work with a multi-document transaction, we need to create the collection first of all. And for it, we verify if the collection exists in our database and, if not, we create it. If we try to create a collection that already exists, the flow will break and throw an exception.
Now, we will do an important code too, specifically a virtual property that we use to facilitate to do some operations:
From the _mongoClient attribute, we retrieve the database and, from the _collection attribute, we retrieve the collection and associate it with the Generic class.
Finally, we build some base operations to change the data in the database like Insert, Update, and Delete:
Some important things:
- We have to pass the _clientSessionHandle for all the methods that do, in fact, the operation, like the InsertOneAsync method.
- To do an Update operation, we build a Lambda Expression, using the ID property. And then, we use a Reflection to retrieve the ID value and we make the filter that will use to do, in fact, the operation.
Example of a specifical repository
Now I show you how we can use the Base Repository to do some other specifically repository. In this case, we’ll build an AuthorRepository.
First, we build the class with the inheritances and the constructor:
When we inherit the BaseRepository class, we force it to make a constructor as shown. Look at how we pass the collection name using a string “author”.
Finally, we build some other MongoDB operations, here we build only specific query operations to get data. It happens because we can get specific data in each moment, but the operations that change the database data (like insert, update, delete) will be always the same in this example.
In this code, I show you some query operations examples:
- We can recover one author searching by id.
- We can recover all authors.
- We can recover all books of a specific author, searching by author’s id, using the Project method.
- We can recover some authors searching by name.
Business rules examples with transactions
Let’s use these repositories to do some business rules and use, in fact, the transactions! For this, we’ll make all business rules in the classes located in the Controller folder.
So, we build a class called BusinessController, like below:
I’ll don’t talk about the Annotations and the inheritances class, because it’s specific for the API. So, look that we use the repositories that we created before and a session _clientSessionHandle that we’ll use to make the transactions. All these attributes we’ll receive on the constructor parameters by dependency injection.
So now, let’s show how we can use a transaction, in fact:
In this case, it’s a POST method, which means that we want to insert a new record on the database. In the method beginning, we need to start a transaction (line 11), then we can make our business rules and, if there is nothing wrong, we can Commit all changes we did on the database. But if something break, an exception will be thrown and the flow will begin on the Catch statement, thereby, we’ll abort all the transaction and nothing in the database will be changed.
Below you can look another example using multi-document transactions. Where we use more than one operation on the database:
The logic is the same as the code shown before.
I expect that you liked this article! If yes, give a like to it. If you have any doubt or any questions comment below.
You can find the complete code here!