[CSHARP-910] BsonInternalException thrown in BsonCreatorMap.CreateInstance Created: 17/Feb/14  Updated: 02/Apr/16  Resolved: 28/Oct/14

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 1.8.3
Fix Version/s: 1.10, 2.0

Type: Bug Priority: Major - P3
Reporter: Norbert Grabowski Assignee: Robert Stam
Resolution: Done Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

Ever since I registered creator for my persistable model, I can no longer deserialize it from database. Driver keeps throwing BsonInternalException from BsonCreatorMap.CreateInstance method.

I found this code fragment to be responsible for this:

else if (!_defaultValues.TryGetValue(elementName, out argument))
{
   // shouldn't happen unless there is a bug in ChooseBestCreator
   throw new BsonInternalException();
}

After further investigation I found the root reason for this problem. It's the BsonCreatorMap.Freeze method, which contains following code fragment:

// compare MetadataTokens because ReflectedTypes could be different (see p. 774-5 of C# 5.0 In a Nutshell)
var memberMap = allMemberMaps.FirstOrDefault(m => m.MemberInfo.MetadataToken == argument.MetadataToken);
if (memberMap == null)
{
   var message = string.Format("Member '{0}' is not mapped.", argument.Name);
   throw new BsonSerializationException(message);
}
elementNames.Add(memberMap.ElementName);

This code matches class property (MemberInfo) to registered BsonMemberMap just by memberInfo.MetadataToken. However, as specified MSDN documentation, this token is not globally unique. It's only unique within specific module.

In my case I have two types:

  • Person defined in BaseAssembly
  • Employee (extends Person) defined in AnotherAssembly

Two types, defined in two assemblies, in one inheritance hierarchy. Two different properties were assigned the same MetadataToken:

  • Person.Code
  • Employee.Name

This line:

var memberMap = allMemberMaps.FirstOrDefault(m => m.MemberInfo.MetadataToken == argument.MetadataToken);

caused the same member map (for Person.Code property) to be added twice (once for Person.Code and once for Employee.Name), due to the same metadata tokens.

So _elementNames has 2x Code property. CreateInstance iterates over this collection, removing objects from values dictionary. First iteration is OK (removes actual value for Code property). Second iteration can't find value for duplicated Code property and throws BsonInternalException.



 Comments   
Comment by Githook User [ 28/Oct/14 ]

Author:

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

Message: CSHARP-910: Compare Module as well as MetadataToken when comparing MemberInfos.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/ac3fab0fb4b815e0afc746225a429f394a77f36b

Comment by Githook User [ 27/Oct/14 ]

Author:

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

Message: CSHARP-910: Compare Module as well as MetadataToken when comparing MemberInfos.
Branch: v1.x
https://github.com/mongodb/mongo-csharp-driver/commit/e2ff865d13c986e55ba80ff488f3f44ec1441b2e

Comment by Marco [ 23/Jun/14 ]

I apparently hit the same problem.

I have a class called UserAccount which works fine. Can insert/find/delete without problems.

Then I introduced a new class called PackageLabel with properties:

public int Id

{ get; set; }
public string Name { get; set; }

public string Title

{ get; set; }
public string Subtitle { get; set; }

public DateTime? UseBy

{ get; set; }
public DateTime LastModified { get; set; }

until now all works fine.

But if I add a property
public DateTime Created

{ get; set; }

to the PackageLabel entity.... here the crazyness begins.

It starts to give me exception of type 'MongoDB.Bson.BsonInternalException' when I try to retrieve one of the UserAccount entities????

That is CRAZY, since the two entities have NOTHING in common. How is it possible that defining a new type of entity has impacts with a completely unrelated one????

Comment by Norbert Grabowski [ 17/Feb/14 ]

I suggest:

  1. Add a message to thrown BsonInternalException, for example:

    throw new BsonInternalException("Could not resolve value for argument '" + elementName + "'");

    It would really help to investigate issues such as mine.

  2. Replace allMemberMaps.FirstOrDefault with allMemberMaps.SingleOrDefault. It will detect mapping ambiguity problems earlier, during application startup.
  3. Modify code using MetadataToken for matching. It seems that adding Module equality criteria to comparison:

    // compare MetadataTokens because ReflectedTypes could be different (see p. 774-5 of C# 5.0 In a Nutshell)
    var memberMap = allMemberMaps.FirstOrDefault(m => m.MemberInfo.MetadataToken == argument.MetadataToken && m.MemberInfo.Module == argument.Module);

    would satisfy MSDN uniqueness criteria. However, I'm not familiar with this reference to "C# 5.0 In a Nutshell" book in code comment. So maybe there's a reason it looks like this currently and issue need to be solved in a different way.

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