Search code examples
c#mongodbmongodb-query

MongoDB document does not map correctly to an object


QUESTION

I have the following problem with the demo code (shown below). When executing this line of code:

var customer = collection.Find(filter).FirstOrDefault();

I get an error:

System.FormatException: An error occurred while deserializing the Header property of class Customer: Element 'id' does not match any field or property of class Header.

System.FormatException: Element 'id' does not match any field or property of class Header.

If I add the [BsonIgnoreExtraElements] decorator to the Customer and Header classes, the query runs without errors, but header.id is set to null.

[Customer]
    Id [string] = "65d5fa85e4eb515ee6f6301a"
    GivenName [string] = "Renata"
    [Header]
        Id [string] = null

MongoDB document:

{
  "_id": {
    "$oid": "65d5fa85e4eb515ee6f6301a"
  },
  "given_name": "Renata",
  "address_filled": false,
  "header": {
    "id": "0945f7cd-16a8-4ea6-b87f-c24e90dcfbc6"
  }
}

Demo code

The purpose of the code below is to query a MongoDB document by the id of its header subdocument using the MongoDB.Driver package and map the return to a C# class (using MongoDB.Bson).

C# classes to map the MongoDB document:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class Header
{
    [BsonElement("id")]
    public string Id { get; set; }
}

public class Customer
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public ObjectId Id { get; set; }

    [BsonElement("given_name")]
    public string GivenName { get; set; }

    [BsonElement("address_filled")]
    public bool AddressFilled { get; set; }

    [BsonElement("header")]
    public Header Header { get; set; }
}

Code to execute MongoDB query

Use the MongoDB.Driver to query the document by the id in the header subdocument:

using MongoDB.Driver;
using System;

class Program
{
    static void Main(string[] args)
    {
        // Connection string and database setup
        var connectionString = "your_connection_string";
        var client = new MongoClient(connectionString);
        var database = client.GetDatabase("customers");
        var collection = database.GetCollection<Customer>("customers");

        // Query to find the document by header id
        var filter = Builders<Customer>.Filter.Eq("header.id", "0945f7cd-16a8-4ea6-b87f-c24e90dcfbc6");

        // Execute the query
        var customer = collection.Find(filter).FirstOrDefault();

        // Display the result
        if (customer != null)
        {
            Console.WriteLine($"Customer Name: {customer.GivenName}");
            Console.WriteLine($"Header ID: {customer.Header.Id}");
        }
        else
        {
            Console.WriteLine("Customer not found.");
        }
    }
}

Explanation

  • MongoClient: creates a connection to your MongoDB instance.
  • GetDatabase: retrieves the database by name.
  • GetCollection: gets the collection named customers.
  • Builders<Customer>.Filter.Eq: creates a filter to find a document where the header.id equals the specified value.
  • Find: executes the query, and FirstOrDefault returns the first matching document or null if no match is found.
  • BsonElement: maps the property to the corresponding field in the MongoDB document.

This example demonstrates querying the MongoDB collection by the header.id and mapping the result to a C# object using the MongoDB.Driver and MongoDB.Bson packages.

Thanks! 🤗


Solution

  • In MongoDB .NET Driver, the Id property (ignore case sensitive) will auto map to the _id field. You can use the [BsonNoId] attribute to prevent the default behavior.

    [BsonNoId]
    public class Header
    {
        [BsonElement("id")]
        public string Id { get; set; }
    }
    

    Alternatively, you should work with BsonClassMap and add it before the GetCollection line.

    BsonClassMap.RegisterClassMap<Header>(cm =>
    {
        cm.AutoMap();
        cm.UnmapField(x => x.Id);
    
        cm.MapMember(x => x.Id)
            .SetElementName("id");
    });