[CSHARP-1447] Serialization fails when implementation of IDictionary member does not have a public parameterless constructor Created: 16/Oct/15 Updated: 25/Jan/18 Resolved: 02/Feb/16 |
|
| Status: | Closed |
| Project: | C# Driver |
| Component/s: | BSON, Serialization |
| Affects Version/s: | 2.0, 2.0.1 |
| Fix Version/s: | 2.3 |
| Type: | Bug | Priority: | Minor - P4 |
| Reporter: | James Hadwen | Assignee: | Robert Stam |
| Resolution: | Done | Votes: | 0 |
| Labels: | regression | ||
| Remaining Estimate: | Not Specified | ||
| Time Spent: | Not Specified | ||
| Original Estimate: | Not Specified | ||
| Attachments: |
|
||||||||
| Issue Links: |
|
||||||||
| Description |
|
When attempting to serialize a property of IDictionary<T,U>, if the implementing class does not contain a public parameterless constructor then serialization will fail with exception:
Example above (stack trace attached) from setting property as return of dict operator in F# Core https://msdn.microsoft.com/en-us/library/ee353774.aspx the implementation of which is the result of an object expression and therefore does not contain a public constructor. Attached is a unit test that works correctly in 1.x but does not work in 2.x (non F# but same principle). Exception is resolvable by changing signature of class DictionaryInterfaceImplementerSerializer<TDictionary, TKey, TValue> to remove the new() constraint, and then changing implementation of DictionaryInterfaceImplementerSerializer<TDictionary, TKey, TValue>.CreateInstance() to return Activator.CreateInstance<TDictionary>() |
| Comments |
| Comment by Stefano Ricciardi [ 27/Jul/16 ] | |||||||||||||||||||||||
|
I also incurred in this problem trying to serialize a ReadOnlyDictionary<string, object>(), which was working previously with the 1.X driver. | |||||||||||||||||||||||
| Comment by Githook User [ 02/Feb/16 ] | |||||||||||||||||||||||
|
Author: {u'username': u'rstam', u'name': u'rstam', u'email': u'robert@robertstam.org'}Message: | |||||||||||||||||||||||
| Comment by Githook User [ 02/Feb/16 ] | |||||||||||||||||||||||
|
Author: {u'username': u'jhadwen', u'name': u'James Hadwen', u'email': u'james.hadwen@sociustec.com'}Message: | |||||||||||||||||||||||
| Comment by James Hadwen [ 19/Oct/15 ] | |||||||||||||||||||||||
|
Thanks for looking at this. The dictionary class in the test is somewhat contrived, it wasn't my intention that the class would be an example of a "known type", i.e. where a custom serializer could be applied at the repository layer; but instead an example of how behaviour that worked previously no longer does. I've attached another test (DictionaryTests.fs Just to summarise my thoughts on this issue
| |||||||||||||||||||||||
| Comment by Robert Stam [ 18/Oct/15 ] | |||||||||||||||||||||||
|
The gist of the problem here is that the custom ConstructorLessDictionary<TKey, TValue> class does not have a public parameterless constructor, so how should the driver create an instance of this class? Well, in this example, it turns out that intended way to create an instance of the ConstructorLessDictionary<TKey, TValue> class is to call the public static factory method called ConstructorReplacement. One way to get the driver to use this public static factory method instead of the non-existent public parameterless constructor is to hook up a simple custom serializer for the custom ConstructorLessDictionary<TKey, TValue> class. It would look like this:
I don't know if this would address your particular need or not, but it does show one way to hook up a public static factory method as an alternative to public parameterless constructor. | |||||||||||||||||||||||
| Comment by Robert Stam [ 18/Oct/15 ] | |||||||||||||||||||||||
|
The .NET driver's serialization design normally requires a public parameterless constructor (used to instantiate the object being deserialized) and settable public properties (used to populate the values of the object being deserialized). When using the BsonClassMapSerializer the BsonClassMap can be configured to you use alternate constructors, but they still must be public. I don't think it would be safe in general to call a private parameterless constructor instead. It's normally private for a reason. I'll continue to look more closely at your provided code samples. |