Motivation
For the sake of this article, let's assume we have a class Vehicle than can be car, motorcycle, etc. We want to store our data in the database in a structure similar to this:
{
"vehicles": [
{
"type": "car",
"model": "Tesla",
"color": "white"
},
{
"type": "motorcycle",
"brand": "Yamaha"
},
{ "type": "plane",
"numberOfSeats": 100,
"previousFlights": [
{ "date": "03-11-2021", "from": "Beirut", "to": "Istanbul" },
{ "date": "21-12-2021", "from": "Paris", "to": "Beirut" }
]
},
{
"type": "car",
"model": "Mercedes",
"model": "2022"
},
]
}
We can, of course, create a class for each vehicle covering all the possible fields. However, this approach is not recommended when the data is not limited to a specific structure, because any new model would require new development to be done. Instead, we can treat a vehicle as a JSON object and store it as is in the database taking advantage of the power NoSQL databases provide us with.
Prerequisite
In this example, I will be using the official MongoDB Driver for C#.
Implementation
To achieve our goal we will create two representations for the vehicle class, one to serve as a database entity, and another one as an API data transfer object.
VehicleEntity
In the database entity, we will represent the content of the vehicle as a BsonArray:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Mohammed.Ezzedine.MongoExample.Entities;
public class VehicleEntity
{
[BsonId]
[BsonRepresentation(BsonType.Int32)]
public int Id { get; set; }
public BsonArray Content { get; set; }
}
VehicleDto
For the API data transfer object, and since BsonArray is a representation scoped for MongoDB, we will use JsonNode. This way, we are not coupling our API behavior to the DB vendor we are using.
using System.Text.Json.Nodes;
namespace Mohammed.Ezzedine.MongoExample.Dtos;
public class VehicleDto
{
public int Id { get; set; }
public JsonNode Content { get; set; }
}
Mapping
When our API receives a request to store a new vehicle in the database, the content will be in the form of a JsonNode, however, we want it to be a BsonArray, and this conversion is not done implicitly. To solve this issue, we can convert back and forth between the twp types as follows:
using System.Text.Json.Nodes;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using Mohammed.Ezzedine.MongoExample.Dtos;
using Mohammed.Ezzedine.MongoExample.Entities;
public class VehicleDataMapping
{
public static VehicleEntity map(VehicleDto dto)
{
return new VehicleEntity
{
Id = dto.Id,
Content = BsonSerializer.Deserialize<BsonArray>(dto.Content.ToString(), null)
};
}
public static VehicleDto map(VehicleEntity entity)
{
return new VehicleDto
{
Id = entity.Id,
Content = JsonArray.Parse(entity.Content.ToString(), null, default)
};
}
}