[CSHARP-100] CSharp driver exception when Saving a an object with an IList<T> property as opposed to List<T> Created: 17/Nov/10  Updated: 02/Apr/15  Resolved: 17/Nov/10

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: 0.9
Fix Version/s: 0.9

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

Visual Studio 2008



 Description   

Long story short, if I call MongoCollection.Save<T>() on a class with
an IList<T> property it throws an exception. If I change it it List<T>
it does not.

Long Explanation where I take actual code, and scrub it:

So I have class that kinda looks like this:

[DataContract]
public class ChildClass : ParentClass
{
[BsonIgnoreIfNull]
[DataMember(Order = 1)]
[BsonKnownTypes(typeof(List<SomeClass>))]
public IList<SomeClass> SomeProperty

{ get; set; }
}


And then I do something like this:

var obj = new ChildClass{
SomeProperty = new List<SomeClass>();
};
_db["someCollection"].Save(obj, SafeMode.True);

Then I get the following
GenericArraySerializer cannot be used with type:
System.Collections.Generic.IList`1[[SomeClass, SomeAssembly
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
at MongoDB.Bson.DefaultSerializer.GenericArraySerializer.VerifyNominalType(Type
nominalType) in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer\\Serializers
GenericArraySerializer.cs:line
113\r\n at MongoDB.Bson.DefaultSerializer.GenericArraySerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer\\Serializers
GenericArraySerializer.cs:line
83\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.SerializeMember(BsonWriter
bsonWriter, Object obj, BsonMemberMap memberMap) in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
221\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
181\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
245\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value) in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
229\r\n at MongoDB.Bson.DefaultSerializer.EnumerableSerializer`1.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer\\Serializers
CollectionSerializersGeneric.cs:line
89\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.SerializeMember(BsonWriter
bsonWriter, Object obj, BsonMemberMap memberMap) in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
221\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
181\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
245\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value) in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
229\r\n at MongoDB.Bson.DefaultSerializer.EnumerableSerializer`1.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer\\Serializers
CollectionSerializersGeneric.cs:line
89\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.SerializeMember(BsonWriter
bsonWriter, Object obj, BsonMemberMap memberMap) in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
221\r\n at MongoDB.Bson.DefaultSerializer.BsonClassMapSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\DefaultSerializer
BsonClassMapSerializer.cs:line
181\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize(BsonWriter
bsonWriter, Type nominalType, Object value, Boolean serializeIdFirst)
in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
245\r\n at MongoDB.Bson.Serialization.BsonSerializer.Serialize[T](BsonWriter
bsonWriter, T value, Boolean serializeIdFirst) in d:
justin's
projects\\mongo-csharp\\Bson\\Serialization
BsonSerializer.cs:line
221\r\n at MongoDB.Driver.Internal.MongoUpdateMessage`2.WriteBody()
in d:
justin's
projects\\mongo-csharp\\Driver\\Internal
MongoUpdateMessage.cs:line
58\r\n at MongoDB.Driver.Internal.MongoRequestMessage.WriteToBuffer()
in d:
justin's
projects\\mongo-csharp\\Driver\\Internal
MongoRequestMessage.cs:line
81\r\n at MongoDB.Driver.Internal.MongoConnection.SendMessage(MongoRequestMessage
message, SafeMode safeMode) in d:
justin's
projects\\mongo-csharp\\Driver\\Internal
MongoConnection.cs:line
324\r\n at MongoDB.Driver.MongoCollection.Update[TQuery,TUpdate](TQuery
query, TUpdate update, UpdateFlags flags, SafeMode safeMode) in
d:
justin's projects\\mongo-csharp\\Driver\\Core
MongoCollection.cs:line
662\r\n at MongoDB.Driver.MongoCollection.Save[TDocument](TDocument
document, SafeMode safeMode) in d:
justin's
projects\\mongo-csharp\\Driver\\Core
MongoCollection.cs:line 584\r\n
at
... ... ... My Code .......


So the fix is as follows:

[DataContract]
public class ChildClass : ParentClass
{
[BsonIgnoreIfNull]
[DataMember(Order = 1)]
- [BsonKnownTypes(typeof(List<SomeClass>))]
- public IList<SomeClass> SomeProperty { get; set; }

+ public List<SomeClass> SomeProperty

{ get; set; }

}



 Comments   
Comment by Robert Stam [ 17/Nov/10 ]

Fixed. Also improved implementation of ArraySerializer (no Invoke any more).

Comment by Justin Dearing [ 17/Nov/10 ]

It took me a bit to understand your comment. I didn't know and IList<T> could hold an array, but the MSDN verified it. The problem for me is that WCF is the real culprit, I'm passing a request contract to th

I actually have no need to use IList<T> as opposed to List<T>, I was just trying to be all polymorphic and using the interface since that seemed like "The Right Thing To Do"™. So changing from IList<T> to List<T> is my quick workaround for me. Seeing this WCF behavior, and the implications, like SomeProperty.Add() throwing NotImplementedException(), I might end up sticking with List<T> for the long term.

Comment by Robert Stam [ 17/Nov/10 ]

Was able to reproduce by adding these two lines:

obj = new ChildClass { SomeProperty = new []

{ new SomeClass(), new SomeClass() }

};
collection.Save(obj, SafeMode.True);

so an easy workaround while this gets fixed is to use Lists instead of arrays when initializing SomeProperty.

Comment by Robert Stam [ 17/Nov/10 ]

I added a unit test for this (CSharp100Tests.cs. under DriverOnlineTests). You can mess with the unit test and see if you can make it fail. Perhaps I didn't duplicate your scenario faithfully enough.

Comment by Robert Stam [ 17/Nov/10 ]

I tried to reproduce and wasn't able to.

Here's part of the code I wrote:

collection.RemoveAll();
var obj = new ChildClass

{ SomeProperty = null }

;
collection.Save(obj, SafeMode.True);
obj = new ChildClass

{ SomeProperty = new List<SomeClass>() }

;
collection.Save(obj, SafeMode.True);
obj = new ChildClass

{ SomeProperty = new List<SomeClass> { new SomeClass() }

};
collection.Save(obj, SafeMode.True);
obj = new ChildClass

{ SomeProperty = new List<SomeClass> { new SomeClass(), new SomeClass() }

};
collection.Save(obj, SafeMode.True);

and here's what the collection looked like in the shell:

> db.csharpxx.find()

{ "_id" : ObjectId("4ce42b7fe447ad36fc456185") } { "_id" : ObjectId("4ce42b7fe447ad36fc456186"), "SomeProperty" : [ ] }

{ "_id" : ObjectId("4ce42b7fe447ad36fc456187"), "SomeProperty" :
[ { } ] }
{ "_id" : ObjectId("4ce42b7fe447ad36fc456188"), "SomeProperty" :
[ { }, { } ] }

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