[CSHARP-1890] Eq should still work when TField is not exactly right (like it used to) Created: 11/Jan/17  Updated: 27/Jan/17  Resolved: 27/Jan/17

Status: Closed
Project: C# Driver
Component/s: API
Affects Version/s: 2.4.1
Fix Version/s: 2.4.2

Type: Bug Priority: Major - P3
Reporter: Marco Serina Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to CSHARP-1891 Distinct should still work when TFiel... Closed

 Description   

Hi,
I have this description on my property and that's fine until version 2.4.0

 
[BsonRepresentation(BsonType.String)]
public List<Organization.OrganizationRole> OrganizationRoles { get; set; }

Organization.OrganizationRole is a Enum.

As soon as we upgrade to 2.4.1, we receive a cast exception during deserialization.

Do we need to setup something different to make it work again?

Thanks a lot!



 Comments   
Comment by Githook User [ 27/Jan/17 ]

Author:

{u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}

Message: CSHARP-1890: Be more flexible in what types are valid for filter builder value arguments.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/7edccb7c8178198b46c444957e1c16447941def0

Comment by Robert Stam [ 14/Jan/17 ]

https://mongodbcr.appspot.com/117160001/

Comment by Robert Stam [ 14/Jan/17 ]

Even though some of these queries could be called invalid because of the mismatch between the field type and the value type, for backward compatibility reasons we're going to work on the changes we made to 2.4.1 to make them more backward compatible.

Filter builder methods will use the field serializer to serialize value arguments if possible, otherwise they will just lookup the serializer from the registry.

Comment by Robert Stam [ 13/Jan/17 ]

This is due to the same underlying issue of CSHARP-1891.

Comment by Marco Serina [ 12/Jan/17 ]

Thanks Robert, got it
I will change the code accordingly

Thanks
-marco

Comment by Robert Stam [ 12/Jan/17 ]

Thanks for the additional information. I can get the same exception you get now using this code:

var builder = Builders<C>.Filter;
var filter = builder.Eq("E", "A");
var registry = BsonSerializer.SerializerRegistry;
var renderedFilter = filter.Render(registry.GetSerializer<C>(), registry).ToJson();

This code is doing the same thing as your code but I've isolated just the problem area.

Here's what's going on: property E is of type List<E> so the filter builder expects that the value will also be a List<E>. In this case the value is a string not a List<E> which results in the exception we see.

The query should be written this way:

var builder = Builders<C>.Filter;
var filter = builder.AnyEq("E", E.A);
var registry = BsonSerializer.SerializerRegistry;
var renderedFilter = filter.Render(registry.GetSerializer<C>(), registry).ToJson();

The AnyEq builder method differs from the Eq method in that it isn't checking whether E is equal to the provided value, but rather whether any of the items in E is equal to the value.

Note also that the value should be the constant E.A, not the string "A", because now that we are using the underlying field serializer the serializer will take care of converting E.A to the correct string.

While you are correct that your existing code was producing the correct filter in earlier versions of the driver, that was by accident and not by design. Once we made the change to start using the correct serializer for the values the application now has to be more precise in which filter builder method it uses and the type of the supplied value. Unfortunately, we can't catch these mismatches at compile time.

Comment by Marco Serina [ 12/Jan/17 ]

Just tried now

      var result = collection.Find(x=> x.OrganizationRoles.Contains(Organization.OrganizationRole.Reseller))
                         .Limit(100);

This is working..is there any change in the way FilterDefinition works?

Cheers

-marco

Comment by Marco Serina [ 12/Jan/17 ]

Hi Robert,
thanks a lot for your quick reply. Yes, you are right, it does not directly depend on serialization but how I build the filter to for finding documents. This is the Exception:

result.ToList()
'result.ToList()' threw an exception of type 'System.InvalidCastException'
    Data: {System.Collections.ListDictionaryInternal}
    HResult: -2147467262
    HelpLink: null
    InnerException: null
    Message: "Unable to cast object of type 'System.String' to type 'System.Collections.Generic.List`1[MongoDbProva.Organization+OrganizationRole]'."
    Source: "MongoDB.Driver"
    StackTrace: "   at MongoDB.Driver.FieldValueSerializerHelper.CastingSerializer`2.Serialize(BsonSerializationContext context, BsonSerializationArgs args, TFrom value)\r\n   at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Serialize[TValue](IBsonSerializer`1 serializer, BsonSerializationContext context, TValue value)\r\n   at MongoDB.Driver.SimpleFilterDefinition`2.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)\r\n   at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOperation[TProjection](FilterDefinition`1 filter, FindOptions`2 options)\r\n   at MongoDB.Driver.MongoCollectionImpl`1.FindSync[TProjection](FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)\r\n   at MongoDB.Driver.FindFluent`2.ToCursor(CancellationToken cancellationToken)\r\n   at MongoDB.Driver.IAsyncCursorSourceExtensions.ToList[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)"
    TargetSite: {Void Serialize(MongoDB.Bson.Serialization.BsonSerializationContext, MongoDB.Bson.Serialization.BsonSerializationArgs, TFrom)}

I get this exception when I specify a filter on that field

var collection = db.GetCollection<Organization>("Organization");
var f = Builders<Organization>.Filter.Eq("OrganizationRoles", "Reseller");
var result = collection.Find(f)
                  .Limit(100);
 
var list = result.ToList();

As I said, this is perfectly working with 2.4.0 so I was wondering if I need to manage the query in a different way.

Thanks a lot again!

-marco

Comment by Robert Stam [ 11/Jan/17 ]

Sorry you are encountering a problem.

I am unable to reproduce this using the following code:

public enum E { A, B }
 
public class C
{
    [BsonRepresentation(BsonType.String)]
    public List<E> E { get; set; }
}
 
class Program
{
    static void Main(string[] args)
    {
        var c = new C { E = new List<E> { E.A, E.B } };
        var json = c.ToJson();
        var r = BsonSerializer.Deserialize<C>(json);
    }
}

The value of the json variable is:

"{ \"E\" : [\"A\", \"B\"] }"

And the r variable contains a correctly rehydrated instance of C.

I suspect your error may actually be only indirectly related to serialization. Can you please provide the line of code that throws the exception and the stack trace?

Generated at Wed Feb 07 21:40:57 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.