timkado
timkado

Reputation: 2052

Protobuf-net error with nested classes: 'Type is not expected, and no contract can be inferred: ...'

I am using Protobuf-net v2.4.4 to serialize a series of nested classes that contain large double arrays. Besides the custom classes, all data types are primitives or arrays/lists of primitives such as strings or double[] and I am not sure why I am getting this error. The error also seems to randomly pop up for my classes. A class serializes fine - after a restart, it stops working and throws the above error. I also was able to serialize one class in a new project but not in my original project. Is there any good way to debug this and to find the source of it? I suspect it is some kind of project setting or conflicting DLLs since the new project obviously has a lot fewer references.

I began serializing each custom class on its own in order to track down the problem but I am not sure if this is reliable given the experience mentioned above. Nevertheless, I am pasting a simplified example of my code below. Classes with the issue are MyClass2 -> MyClass5 and MyClass3. But then I cannot see anything out of the ordinary in these classes.

Any suggestions would be greatly appreciated.

[ProtoContract] public class MyParentClass
{
    [DataMember] [ProtoMember(1)] public MyClass1 Settings { get; set; } // On its own this class serializes fine
    [DataMember] [ProtoMember(2, OverwriteList = true)] public List<MyClass2> Zones { get; set; } = new List<MyClass2>(); // ProtoBuf does not like this class
    ...
    [DataMember]  [ProtoMember(4)]  public MyClass3 Results { get; set; } // This serializes fine in one project but not in conjunction with the rest here
}

[ProtoContract]  public class MyClass2
{
    [DataMember] [ProtoMember(5)] public MyClass4 Settings { get; set; } // On its own this class serializes fine
    [DataMember] [ProtoMember(6, OverwriteList = true)] public List<MyClass5> faces { get; set; } = new List<MyClass5>(); // ProtoBuf does not like this one
    [DataMember] [ProtoMember(7)] public MyClass6 Result { get; set; } // This class looks similar to MyClass3
    ...
}
[ProtoContract] public class MyClass5
{
    [DataMember] [ProtoMember(4)] public Enum1 Bcond { get; set; } = Enum1._UNSET_; // These enums are also decorated with ProtoContract and ProtoEnum
    ...
    [DataMember] [ProtoMember(7)] public string Twin { get; set; } = "";
    ...
}
[ProtoContract] public class MyClass3
{
    [DataMember] [ProtoMember(1)] public double energy { get; set; }
    ...
    [DataMember] [ProtoMember(3, OverwriteList = true)] public double[] MoreEnergy { get; set; } = new double[8760];
    ...
    [DataMember] [ProtoMember(17, OverwriteList = true)] public double[] EvenMoreDataButNotSetAutomatically { get; set; }
}

Upvotes: 1

Views: 1046

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062502

Fundamentally, I can't make it repro what you're seeing; taking the code above and playing with it locally, it just works.

However! There are some potentially odd timing scenarios if you start serializing types concurrently - typical on web-servers etc. A good fix here would be to make sure that all the prep work happens as early as possible, for example in your startup code you could do:

Serializer.PrepareSerializer<MyParentClass>();

(and perhaps a few others)


Note that the new double[8760] is a bad idea here, as protobuf-net will run the constructor and then swap the array; you might want to defer that and create it in your own code (or there are various other tricks that can be used). You may also want to use "packed" arrays for this one, i.e.

[ProtoMember(3, OverwriteList = true, IsPacked = true)]

on the MoreEnergy property.

In v3 "packed" encoding is enabled by default when applicable, so it is essentially opt-out; in v2.* it is opt-in. So: best to be explicit.

Upvotes: 2

Related Questions