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