2 / 2
Jun 2024

Hello there!

I am working on our OpenSource project for ValueObjects. We have a special kind of ValueObject that only hats one primitive (int, string, etc.) value. A typical primitive looks like this:

public sealed class Age : PrimitiveValueObject<Age, int> { /// <inheritdoc /> public Age(int value) : base(value) { } }

The base class provides the Value property. In this case of type int.

The entity class looks like this:

public class Person { public ObjectId Id { get; set; } public string Name { get; set; } public Age Age { get; set; } }

I implemented a generic custom serializer to allow storing the value just as a simple integer and not as a complex type. This works fine, but when we try to query data it caused the error: “Serializer does not represent members as fields”.

The topic here fixed this problem, but now I don’t get any value back. The result is just null.

I could confirm, that the Deserialize method is now no longer called.

The serializer class looks like this:

public sealed class PrimitiveValueObjectSerializer<TValueObject, TValue> : SerializerBase<TValueObject>, IBsonDocumentSerializer where TValueObject : PrimitiveValueObject<TValueObject, TValue> where TValue : IComparable { /// <inheritdoc /> public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValueObject value) { if(value is null) { context.Writer.WriteNull(); } else { BsonSerializer.Serialize(context.Writer, value.Value); } } /// <inheritdoc /> public override TValueObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { if(context.Reader.CurrentBsonType == BsonType.Null) { context.Reader.ReadNull(); return null; } TValue value = BsonSerializer.Deserialize<TValue>(context.Reader); object instance = Activator.CreateInstance(args.NominalType, BindingFlags.Public | BindingFlags.Instance, null, [value], null); return (TValueObject)instance; } /// <inheritdoc /> public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) { if(memberName == "Value") { IBsonSerializer<TValue> serializer = BsonSerializer.LookupSerializer<TValue>(); serializationInfo = new BsonSerializationInfo(memberName, serializer, typeof(TValue)); return true; } serializationInfo = null; return false; } }

In the example BsonSerializer.LookupSerializer<TValue>() correctly gets the Int32Serializer instance for the TValue type (in this case int).

I tried the the following query types:

Person stringFilterResult = await collection .Find(Builders<Person>.Filter.Lt("Age.Value", 40)) .FirstOrDefaultAsync(); Person expressionFilterResult = await collection .Find(Builders<Person>.Filter.Lt(p => p.Age.Value, 40)) .FirstOrDefaultAsync(); Person linqFilterResult = await collection .AsQueryable() .Where(x => x.Age.Value < 40) .FirstOrDefaultAsync();

Every query lust return null. Not error, no exception.

When I use the stringly-typed filter without the navigation to the Value property, I get the correct instance back:

Person stringFilterResult = await collection .Find(Builders<Person>.Filter.Lt("Age", 40)) .FirstOrDefaultAsync();

What am I missing here? How can I fix this?

Cheers,
Matthias

Hi @MadEyeMatt,

Thanks for raising this question. This specific use case is unfortunately not supported by the Driver yet. We only support nullableField.Value. I’ve created a ticket CSHARP-5161 to track this work. Please feel free to comment there.

Thanks,

Rishit.