I've been struggling with the exception 'No parameterless constructor found for [type]' for the past few hours. Now I created a simple unit test which should mirror what I've got in the application and it seems that this exception is thrown when I don't do stream.Position = 0.
Also, I don't get this exception in any case when the object is just a standard class (not derived from an abstract one).
See the code below:
run it as is - it will break saying that no constructor could be found for Base
uncomment stream.Position = 0 and it will be fine
comment the line out again, change the Derived class to not inherit from Base and uncomment the only property in the class, run it - it won't break (but obviously the Name will be null)
Can someone explain why this works this way ? Why #1 throws (or why #3 doesn't) and why this message?
[Test]
public void CanSerialize_Derived()
{
var derived = new Derived() {Name = "ngf"};
var stream = new MemoryStream();
Serializer.Serialize(stream, derived);
//stream.Position = 0;
var deserializedInstance = Serializer.Deserialize<Derived>(stream);
}
[ProtoContract]
[ProtoInclude(9, typeof(Derived))]
public abstract class Base
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
public class Derived : Base
{
//[ProtoMember(1)]
//public string Name { get; set; }
}
A stream of length zero is valid in protobuf-net; in protobuf-net all serialization starts at the root type of that DTO inheritance tree, so it would start at Base - and until it can agree that the data actually contains a Derived, it won't believe you - and will try to work with Base. So that is why #1 throws.
Obviously, if you leave the stream at the end, the length of data available to be deserialized is zero. Which is why #2 passes.
If you remove the inheritance, then the root of that inheritance tree is Derived; that is a fundamentally breaking change, but the difference in terms of inheritance is that it is no longer trying to deserialize an abstract type without any information. So that is why #3 fixes it (for bad reasons).
The key takeaway here is that inheritance is implemented as information. The only thing it assumes, unless there is information, is that the object is of the type at the root of the inheritance tree.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With