Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-4517

System.InvalidCastException when using custom operator in filter

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Unknown Unknown
    • 2.19.1
    • Affects Version/s: 2.19.0
    • Component/s: LINQ3
    • Labels:
      None
    • Not Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      Summary

      I created a custom Id type that wraps an integer. I implemented the equality operators for this type. The problem is, when I want to filter in mongoDB with linq and the expression includes this custom operator, the expression translator throws an error.

      MongoDB driver version 2.19.0, .NET version 7.0

      How to Reproduce

      Code:

      using MongoDB.Bson.Serialization;
      using MongoDB.Bson.Serialization.Attributes;
      using MongoDB.Bson.Serialization.Serializers;
      using MongoDB.Driver;
      
      int idToFind = 5;
      
      var filter = Builders<MyDocument>.Filter.Where(x => x.Id == idToFind);
      var renderedFilter = filter.Render(BsonSerializer.LookupSerializer<MyDocument>(), BsonSerializer.SerializerRegistry);
      Console.WriteLine(renderedFilter);
      
      public class MyDocument
      {
      	public MyId Id { get; set; }
      	public string Name { get; set; }
      }
      
      [BsonSerializer(typeof(MyIdSerializer))]
      public class MyId
      {
      
      	public MyId(int id)
      	{
      		Id = id;
      	}
      
      	public int Id { get; }
      
      	public static bool operator ==(int id, MyId other) => id == other.Id;
      	public static bool operator ==(MyId id, int other) => id.Id == other;
      	public static bool operator !=(int id, MyId other) => !(id == other);
      	public static bool operator !=(MyId id, int other) => !(id == other);
      }
      
      public class MyIdSerializer : SerializerBase<MyId>
      {
      	public override MyId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
      	{
      		return new MyId(context.Reader.ReadInt32());
      	}
      
      	public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, MyId value)
      	{
      		context.Writer.WriteInt32(value.Id);
      	}
      }
       

      Exception:

       

      Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'MyId'.
         at MongoDB.Bson.Serialization.Serializers.SerializerBase`1.MongoDB.Bson.Serialization.IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, Object value)
         at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Serialize(IBsonSerializer serializer, BsonSerializationContext context, Object value)
         at MongoDB.Driver.Linq.Linq3Implementation.Misc.SerializationHelper.SerializeValue(IBsonSerializer serializer, Object value)
         at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionTranslators.ComparisonExpressionToFilterTranslator.Translate(TranslationContext context, BinaryExpression expression)
         at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionToFilterTranslator.TranslateUsingQueryOperators(TranslationContext context, Expression expression)
         at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionToFilterTranslator.Translate(TranslationContext context, Expression expression, Boolean exprOk)
         at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionToFilterTranslator.TranslateLambda(TranslationContext context, LambdaExpression lambdaExpression, IBsonSerializer parameterSerializer, Boolean asRoot)
         at MongoDB.Driver.Linq.Linq3Implementation.LinqProviderAdapterV3.TranslateExpressionToFilter[TDocument](Expression`1 expression, IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
         at MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider)
         at MongoDB.Driver.FilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
         at Program.<Main>$(String[] args) in C:\src\MongoTest\MongoTest\Program.cs:line 9 

      Additional Background

      The issue is probably in ComparisonExpressionToFilterTranslator. It tries to use the serializer of the leftExpression for serializing the right operand:

       

      https://github.com/mongodb/mongo-csharp-driver/blob/5d1353dc000613c46184636c8a30c86c46970905/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ComparisonExpressionToFilterTranslator.cs#L78-L80

       

      var field = ExpressionToFilterFieldTranslator.Translate(context, leftExpression);
      var serializedComparand = SerializationHelper.SerializeValue(field.Serializer, comparand);
      return AstFilter.Compare(field, comparisonOperator, serializedComparand); 

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            fussatok@gmail.com x y
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: