I have a legacy database with a collection with the following structure (simplified):
_id : ObjectId
Id: string
Value: Int32
And I have my C# class like this:
public string Id {get; set;}
public int Value {get; set;}
How can I map my class property Id with the Id (string) field in the collection, and not map with _id (ObjectId)?
I have tested many approaches but I usually got this exception message: Cannot deserialize a ‘String’ from BsonType ‘ObjectId’.
By default, the C# driver maps the properties Id , id , and _id to the underlying _id database field. But you can override this behavior via attributes (e.g. [BsonId] ), configuration (BsonClassMap.SetIdMember ), or convention.
Note that the “Id member” is the field _id required by MongoDB. In this case, Id is just some arbitrary field in the database, but MongoDB still requires an _id field, which will populate with a new ObjectId if one is not provided.
So, I added the [BsonId] attribute and define the ObjectId _id in the schema and it worked. Sharing the code for your reference:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization;
public class MyClass
{
[BsonId]
public ObjectId _id { get; set; }
public string Id { get; set; }
public int Value { get; set; }
}
public class Program
{
static async Task Main(string[] args)
{
// Set up a MongoDB client and database
var client = new MongoClient("mongodb://localhost:27017/test");
var database = client.GetDatabase("csharp");
// Get a reference to the "mycollection" collection
var collection = database.GetCollection<BsonDocument>("dotnetcore");
// Insert a sample document into the collection
var myObject = new MyClass { _id = ObjectId.GenerateNewId(), Id = "foo", Value = 42 };
var document = myObject.ToBsonDocument();
await collection.InsertOneAsync(document);
// Query the collection for documents and deserialize them into instances of MyClass
var filter = Builders<BsonDocument>.Filter.Empty;
var documents = await collection.Find(filter).ToListAsync();
var myObjects = new List<MyClass>();
foreach (var y in documents)
{
var deserializedObject = BsonSerializer.Deserialize<MyClass>(y);
myObjects.Add(deserializedObject);
}
// Do something with the deserialized objects
foreach (var x in myObjects)
{
Console.WriteLine($"_id: {x._id}, Id: {x.Id}, Value: {x.Value}");
}
}
}
It returns the output:
_id: 6423c91b114e33a0106407be, Id: foo, Value: 42
However, there may be places where this mapping will not work as expected. For example, if this type is nested as a subdocument or contained in a list or dictionary. While it may work in some cases and maybe it won’t. Our recommended approach is to rename the Id database field to something more meaningful such as CustomerKey.