[CSHARP-1759] cannot work with struct object Created: 30/Aug/16  Updated: 15/Jan/17  Resolved: 15/Jan/17

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

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

Windows10



 Description   

There are following issues:

1. Cannot save struct in db as field _id gives this exception:

{BsonSerializationException: SetDocumentId cannot be used with value type UserQuery+Family.}

2. struct object is stored properly when ObjectId field is removed (in my case this was missing when was storing for first time, but added later as was unable to get it back - see below)

3. BsonSerializationException: Value class UserQuery+Family cannot be deserialized. that happns when this struct object is read from db.

4. When I convert struct to class - it works fine .... but as my application is multi thread I prefer use threads to deal with false sharing on cpu cache.

linqPad code

void Main()
{
TestA();
TestB();
}
 
void TestA()
{
	var client = new MongoClient("mongodb://localhost:27017");
	var database = client.GetDatabase("testError");
	
	var collection = database.GetCollection<Family>("driverStruct");
	
	var a = new Family{
		Kids = new List<Child> {
		new Child{ givenName = "Child 1" + DateTime.Now.ToString(),dateOfBirth=DateTime.Now.AddDays(-2800), IsAlive=true},
		new Child{ givenName = "Child 2" + DateTime.Now.ToString(),dateOfBirth=DateTime.Now.AddDays(-822), IsAlive=false}
		}
	};
	
	collection.InsertOne(a);  // throws error when ObjectId is uncommented
	collection.AsQueryable().ToList().Dump(); //throws error 
}
 
 
void TestB()
{
	var client = new MongoClient("mongodb://localhost:27017");
	var database = client.GetDatabase("testError");
	
	var collection = database.GetCollection<FamilyClass>("driverClass");
	
	var a = new FamilyClass{
		Kids = new List<ChildClass> {
		new ChildClass{ givenName = "Child 1" + DateTime.Now.ToString(),dateOfBirth=DateTime.Now.AddDays(-2800), IsAlive=true},
		new ChildClass{ givenName = "Child 2" + DateTime.Now.ToString(),dateOfBirth=DateTime.Now.AddDays(-822), IsAlive=false}
		}
	};
	
	collection.InsertOne(a);
	collection.AsQueryable().ToList().Dump();
}
public struct Family{
	public ObjectId _id;
	public List<Child> Kids;
}
 
public struct Child{
    public DateTime dateOfBirth { get; set; }
    public string givenName { get; set; }
	public bool IsAlive { get; set; }
}
 
 
public class FamilyClass{
	public ObjectId _id;
	public List<ChildClass> Kids;
}
 
public class ChildClass{
    public DateTime dateOfBirth { get; set; }
    public string givenName { get; set; }
	public bool IsAlive { get; set; }
}
 
 
 
=================================
db.getCollection('driver').find({})
 
/* 1 */
{
    "_id" : ObjectId("57c53dd9197ecf527c31058c"),
    "Kids" : [ 
        {
            "dateOfBirth" : ISODate("2008-12-30T09:03:37.526Z"),
            "givenName" : "Child 18/30/2016 9:03:37 AM",
            "IsAlive" : true
        }, 
        {
            "dateOfBirth" : ISODate("2014-05-31T08:03:37.527Z"),
            "givenName" : "Child 28/30/2016 9:03:37 AM",
            "IsAlive" : false
        }
    ]
}
 
 
===================================================
db.getCollection('driverClass').find({})
 
/* 1 */
{
    "_id" : ObjectId("57c5412734780e1ac09c96b2"),
    "Kids" : [ 
        {
            "dateOfBirth" : ISODate("2008-12-30T09:17:43.406Z"),
            "givenName" : "Child 18/30/2016 9:17:43 AM",
            "IsAlive" : true
        }, 
        {
            "dateOfBirth" : ISODate("2014-05-31T08:17:43.406Z"),
            "givenName" : "Child 28/30/2016 9:17:43 AM",
            "IsAlive" : false
        }
    ]
}



 Comments   
Comment by Robert Stam [ 01/Sep/16 ]

The .NET driver has partial supports for structs. In particular, we don't support automatically mapping structs for deserialization (i.e. the BsonClassMapSerializer does not support structs). The reason is that structs are passed by value instead of by reference and the helper methods that BsonClassMapSerializer uses to alter the values of an object that is being deserialized would be altering a temporary copy of the struct instead.

That being said, the driver itself does not inherently have problems with structs. Just that if you want to use a struct you will probably have to write a custom serializer. For example, Int32 is a struct and the Int32Serializer is an example of a serializer that handles structs. See classes that derive from StructSerializerBase for more examples of custom serializers that handle structs.

The SetDocumentId method that is used (if necessary) to set the _id value of a document that is being inserted into the database also does not support structs, for the same reason that serializers don't. The struct would be passed by value to SetDocumentId and the _id would be changed on the temporary copy instead of the original. This is easy to work around though, just set the _id yourself before calling InsertOne.

I would also recommend against using struct for your top level document. Structs are intended to be used for small value oriented types like Int32 and ObjectId, not for complex types like a top level document.

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