Protobuf-net serialization error = "Type cannot be changed after creating serializer"

The following scenario seems to throw an exception in Protobuf.net during deserialization. Did I do something wrong? Is there any way around this?

[ProtoContract] [ProtoInclude(2, typeof(Ant))] [ProtoInclude(3, typeof(Cat))] public interface IBeast { [ProtoMember(1)] string Name { get; set; } } [ProtoContract] public class Ant : IBeast { public string Name { get; set; } } [ProtoContract] public class Cat : IBeast { public string Name { get; set; } } [ProtoContract] [ProtoInclude(1, typeof(AntRule1))] [ProtoInclude(2, typeof(AntRule2))] [ProtoInclude(3, typeof(CatRule1))] [ProtoInclude(4, typeof(CatRule2))] public interface IRule<T> where T : IBeast { bool IsHappy(T beast); } [ProtoContract] public class AntRule1 : IRule<Ant> { public bool IsHappy(IAnt beast) { return true; } } [ProtoContract] public class AntRule2 : IRule<Ant> { public bool IsHappy(IAnt beast) { return true; } } [ProtoContract] public class CatRule1 : IRule<Cat> { public bool IsHappy(ICat beast) { return true; } } [ProtoContract] public class CatRule2 : IRule<Cat> { public bool IsHappy(ICat beast) { return true; } } public class TestSerialization { public void Serialize() { var antRules = new List<IRule<Ant>>(); antRules.Add(new AntRule1()); antRules.Add(new AntRule2()); var catRules = new List<IRule<Cat>>(); catRules.Add(new CatRule1()); catRules.Add(new CatRule2()); using (var fs = File.Create(@"c:\temp\antRules.bin")) { ProtoBuf.Serializer.Serialize(fs, antRules); fs.Close(); } using (var fs = File.OpenRead(@"c:\temp\antRules.bin")) { List<IRule<Ant>> list; list = ProtoBuf.Serializer.Deserialize<List<IRule<Ant>>>(fs); fs.Close(); } using (var fs = File.Create(@"c:\temp\catRules.bin")) { ProtoBuf.Serializer.Serialize(fs, catRules); fs.Close(); } using (var fs = File.OpenRead(@"c:\temp\catRules.bin")) { List<IRule<Cat>> list; list = ProtoBuf.Serializer.Deserialize<List<IRule<Cat>>>(fs); fs.Close(); } } } 
+4
source share
1 answer

Ultimately, I suspect the problem is here:

  [ProtoContract] [ProtoInclude(1, typeof(AntRule1))] [ProtoInclude(2, typeof(AntRule2))] [ProtoInclude(3, typeof(CatRule1))] [ProtoInclude(4, typeof(CatRule2))] public interface IRule<T> where T : IBeast 

This suggests that for any T , IRule<T> has 4 children. This has a side effect, saying that if you have more than one T , each of AndRule1 ... CatRule2 each of them has "n" parents, which is not very good. Assume instead that IRule<Ant> has 2 ant -rules, etc. (In the end, I doubt that CatRule1 indeed an implementation of IRule<Ant> ). Currently, this can only be expressed through RuntimeTypeModel , since attributes will always be applied to all T :

 [ProtoContract] public interface IRule<T> where T : IBeast 

and

 // note these are unrelated networks, so we can use the same field-numbers RuntimeTypeModel.Default[typeof(IRule<Ant>)] .AddSubType(1, typeof(AntRule1)).AddSubType(2, typeof(AntRule2)); RuntimeTypeModel.Default[typeof(IRule<Cat>)] .AddSubType(1, typeof(CatRule1)).AddSubType(2, typeof(CatRule2)); 

and then it works. Please note that configuration should only be done once, usually at application startup.


Thinking about this, I could probably just test at runtime, and in the case of generics, just ignore any that don't apply - by which I mean when evaluating IRule<Dog> , consider only specific types if they implement IRule<Dog> . I'm still in two minds.

+3
source

All Articles