How to retrieve document with nested document in c#

Hello everyone,

Greetings!

I am completely new in mongodb and currently building an api that retrieves data with nested document via c#.

Here’s my sample code snippet.

/// <summary>
/// Tickets Data Model
/// </summary>
[BsonIgnoreExtraElements]
public class Ticket
{
    [BsonId]
    [BsonElement("_id")]
    [JsonPropertyName("_id")]
    [BsonRepresentation(BsonType.ObjectId)]
    public ObjectId TicketId { get; set; }

    [BsonElement("datetime")]
    [JsonPropertyName("datetime")]
    public string? DateCreated { get; set; } = null;

    [BsonElement("lasttouchedTime")]
    [JsonPropertyName("lasttouchedTime")]
    public string? LastUpdated { get; set; } = null;

    [BsonElement("from")]
    [JsonPropertyName("from")]
    public string? TicketFrom { get; set; } = null;

    public Visitor? VisitorInfo { get; set; } = null;
}


/// <summary>
/// Tickets Visitor data sub-model
/// </summary>
[BsonIgnoreExtraElements]
public class Visitor//: IEmbeddedObject
{
	[BsonElement("name")]
	[JsonPropertyName("name")]
	public string? Name { get; set; } = null;

	[BsonElement("email")]
	[JsonPropertyName("email")]
	public string? Email { get; set; } = null;
}

public List<Ticket> GetData()
{
var database = client.GetDatabase("ticketsDB");
  var collection = database.GetCollection<Ticket>("tickets");
	
	var dateTime1 = string.Format("{0:yyyy-MM-dd}T00:00.000Z", Convert.ToDateTime("2023-01-01"));
	var dateTime2 = string.Format("{0:yyyy-MM-dd}T23:59:59.000Z", Convert.ToDateTime("2023-01-01"));
	
	var filter = new BsonDocument
	{
		{"datetime" , new BsonDocument {
			{ "$gte" , dateTime1},
			{ "$lte" , dateTime2}
		}}
	};
	var result = collection.Find(filter).ToList();

return result;
}

I was able to get records but the field Visitor has no value returned (i,e null).
Any help on how I could get the values of Visitor field of the particular Ticket document?

Thank you very much and all your inputs will be highly appreciated.

Best Regards,
Pas

The .NET/C# Driver uses a lot of conventions. For example if the C# property name and database field name match, you don’t need to specify [BsonElement("SomeField"]). As well if your C# properties use PascalCase, but your database uses camelCase, you can configure the CamelCaseConvention:

var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

As well if you name your _id property as Id, you don’t have to explicitly mark it with [BsonId]. And since your BSON representation matches your C# type (both being ObjectId), you can omit the [BsonRepresentation(BsonType.ObjectId)] attribute.

Regarding why VisitorInfo shows as null, you have a property named VisitorInfo, but I suspect that in the database it is using the camel-cased visitorInfo. Since the property is nullable and database field name doesn’t match the C# property name, the property is null.

In general I don’t recommend using [BsonIgnoreExtraElements] unless it is really necessary. Without this attribute, you would have received a deserialization error stating that a property named visitorInfo could not be found, which would have highlighted the mismatch between VisitorInfo C# property and the visitorInfo database field name.

There are situations where [BsonIgnoreExtraElements] is useful, but these are advanced serialization use cases and shouldn’t be how you typically map C# types to the database.

Hope that helps!

Sincerely,
James

Hello James,

Greetings!

Thank you very much for your response.
It gives me more ideas in configuring the data properties.

Actually, giving the Visitor its BsonElement name fixed the problem.

And I wanted to have the exact string value of the TicketId so I need to specify its BsonRepresentation and declare the property as string.

BsonIgnoreExtraElements also helps me to retrieve only the specified document fields I wanted.
It is just that you have to specify the exact name (CamelCase) as it was presented in the collections.

/// <summary>
/// Tickets Data Model
/// </summary>
[BsonIgnoreExtraElements]
public class Ticket
{
    [BsonId]
    [BsonElement("_id")]
    [JsonPropertyName("_id")]
    [BsonRepresentation(BsonType.ObjectId)]
    public required string TicketId { get; set; }

    [BsonElement("datetime")]
    [JsonPropertyName("datetime")]
    public string? DateCreated { get; set; } = null;

    [BsonElement("lasttouchedTime")]
    [JsonPropertyName("lasttouchedTime")]
    public string? LastUpdated { get; set; } = null;

    [BsonElement("from")]
    [JsonPropertyName("from")]
    public string? TicketFrom { get; set; } = null;

    [BsonElement("visitor")]
    [JsonPropertyName("visitor")]
    public Visitor? Visitor { get; set; } = null;
}

/// <summary>
/// Tickets Visitor data sub-model
/// </summary>
[BsonIgnoreExtraElements]
public class Visitor
{
    [BsonElement("name")]
    [JsonPropertyName("name")]
    public string? VisitorName { get; set; } = null;

    [BsonElement("email")]
    [JsonPropertyName("email")]
    public string? VisitorEmail { get; set; } = null;
}

Thank you very much and you may close this thread.

Best Regards,
Pas