[CSHARP-1957] UpdateDefinitionBuilder fails when stated type does not match field type Created: 05/Apr/17  Updated: 01/May/17  Resolved: 10/Apr/17

Status: Closed
Project: C# Driver
Component/s: Linq
Affects Version/s: 2.4.2, 2.4.3
Fix Version/s: 2.4.4

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

Windows


Attachments: Text File Program.cs    

 Description   

When using UpdateDefinitionBuilder to update a document property of any enumerable parsing in an object, the serializer will change the property from type array to type object adding _t and _v.

Adding these unit tests to UpdateDefinitionBuilderTests will expose the problem:

[Fact] // This test succeeds
public void Set_Untyped_String()
{
	var subject = CreateSubject<Person>();
	var firstName = "Linus";
 
	Assert(subject.Set(x => x.FirstName, firstName), "{$set: {fn: 'Linus'}}");
 
	var subject2 = CreateSubject<Person>();
	object firstName2 = "Julius";
 
	Assert(subject2.Set(x => x.FirstName, firstName2), "{$set: {fn: 'Julius'}}");
}
 
[Fact] // This test fails on the second assert
public void Set_Untyped_List()
{
	var subject = CreateSubject<Person>();
 
	var pets = new List<Pet> { new Pet { Name = "Tiger" } };
	Assert(subject.Set(x => x.Pets, pets), "{$set: { 'pets' : [{ 'name' : 'Tiger' }] }}");
 
	var subject2 = CreateSubject<Person>();
 
	object pets2 = new List<Pet> {new Pet {Name = "Tiger"}};
	Assert(subject2.Set(x => x.Pets, pets2), "{$set: { 'pets' : [{ 'name' : 'Tiger' }] }}");
}

I have also attached a small .net program that simulates what I am doing in order to get this error.



 Comments   
Comment by Githook User [ 10/Apr/17 ]

Author:

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

Message: CSHARP-1957: More tests added.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/5ed128e00a9c74f4bfed36f5aa0d39b308a89657

Comment by Githook User [ 10/Apr/17 ]

Author:

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

Message: CSHARP-1957: New test added.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/3d2c0ef3fde6c1db8547eb331a89017a85e18ff8

Comment by Githook User [ 10/Apr/17 ]

Author:

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

Message: CSHARP-1957: Use correct nominal type if value can be converted to field type.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/54941e4724ae28c486469b6c9b589509b8d8fc25

Comment by Robert Stam [ 07/Apr/17 ]

This isn't directly related to enumerables.

I can reproduce the essence of the issue using these simple classes:

public class C
{
    public int Id { get; set; }
    public D D { get; set; }
}
 
public class D
{
    public string X { get; set; }
}

and this code:

var builder = Builders<C>.Update;
var update = builder.Set<object>("D", new D { X = "x" }); // NOTE: the "D" field is actually of type D but we are forcing <object> here, which is a bit of a lie

which results in this update statement being generated:

"{ \"$set\" : { \"D\" : { \"_t\" : \"D\", \"X\" : \"x\" } } }"

This issue is only occurring because the Set statement was forced to use type <object> instead of the actual type which is <D>.

Comment by Martin Lobger [ 05/Apr/17 ]

Finally figured out why the Program.cs was working in 2.4.1

It was because I was using an interface IEnumerable instead of a concrete implementation. If I change IEnumerable<UserData> to List<UserData> 2.4.1 will fail as well.

So in that sense, 2.4.2 is more consistent. But it leads down to the EnumerableSerializerBase<TValue, TItem> class in the method:

public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)

I suggest changing the line

if (actualType == args.NominalType)

to

if (args.NominalType.IsAssignableFrom(actualType))

RETRACTION: This will fix it for 2.4.1 but apparently not for 2.4.3

Comment by Martin Lobger [ 05/Apr/17 ]

When running Program.cs the document looks like this in the mongodb after the insert operation:

{ 
    "_id" : ObjectId("58e4a9134d492e20d47af47f"), 
    "Name" : "The Name", 
    "UserData" : [
        {
            "UserId" : BinData(3, "dC5RTcAWyUintpwZqIMcWw=="), 
            "Resource" : NumberInt(1)
        }
    ]
}

And after the update the document looks like this:

{ 
    "_id" : ObjectId("58e4a9134d492e20d47af47f"), 
    "Name" : "The New Name", 
    "UserData" : {
        "_t" : "System.Collections.Generic.List`1[[Mongo.Test.UserData, Mongo.Test]]", 
        "_v" : [
            {
                "UserId" : BinData(3, "dC5RTcAWyUintpwZqIMcWw=="), 
                "Resource" : NumberInt(2)
            }
        ]
    }
}

Comment by Martin Lobger [ 05/Apr/17 ]

This little console application works when using nuget mongo.driver 2.4.1 but fails when using nuget mongo.driver 2.4.2 or later

Comment by Martin Lobger [ 05/Apr/17 ]

I just realized that the unit tests I suggested, also fails on v2.4.1

However, the Program.cs will behave as (I would) expected using Nuget Mongo.Driver v2.4.1 but introduces the _t & _v when run using Nuget Mongo.Driver v2.4.2 and forward.

The problem is not (de)serializing the document afterwards, the problem is that all "find" operation will fail after the update.

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