Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserialization failed on array of objects that implements IEquatable<T> #200

Open
Arkatufus opened this issue Jan 5, 2021 · 0 comments
Labels

Comments

@Arkatufus
Copy link
Contributor

Original issue: akkadotnet/akka.net#4692

Bug description:
An error occurred while serializing collection of objects implementing IEquatable<T> with one object that passes equality with another object in the same collection. For the second object that passes equality, ObjectSerializer is used instead of FromSurrogateSerializer. This causes the error System.InvalidCastException: Unable to cast object of type [SurrogateType] to type [RealType].

Preliminary code assessment:
The bug was actually caused by a few factor:

  1. Data marked as ObjectReferenceSerializer.Manifest does not honor the proper custom surrogate deserialization flow.

    • This is caused by a miss-cache of tracked deserialized objects. By definition, objects deserialized from the data stream are supposed to be tracked after they are converted to their original type by the FromSurrogateSerializer. This is not what happened in code; the surrogate object is the one that is being cached inside the DeserializeSession instead.
  2. The use of Dictionary<object, int> in the SerializerSession class.

    • Dictionary<K, V> uses the the object Equal() equality method to determine whether 2 keys are considered equal. By definition, the serializer should flag an object as a duplicate and mark it as an object reference when PreserveObjectReference is set to true in the serializer option. With a Dictionary, this is true when it is using the default object.Equal() method, as it relies on reference equality between 2 objects to determine if 2 objects are the same, but when used with objects that overrides the object.Equal() method, this will no longer be true because the equality will be done by value instead of by reference.
    • A similar object that passes the equality check will be marked as a reference duplicate, even when it is not the same instance of a type. The object will then be marked with the ObjectReferenceSerializer.Manifest manifest, instead of the correct ObjectSerializer.ManifestIndex manifest.
    • With a proper deserializing session object caching (see 1 above), this would be a silent bug where 2 or more instances of an object that are considered equal by Dictionary will be deserialized as references to a single instance of an object. With the malfunctioning object caching, the surrogate class instance were instead retrieved from the session cache, causing the InvalidCastException in the case of ArraySerializerFactory because it will fail to assign the surrogate object into the array index.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant