Если у меня есть тестовая настройка как
[ProtoContract]
[ProtoInclude(1, typeof(DecoratorDerived))]
public class DecoratorBase
{
[ProtoMember(2)]
private int baseValue1;
private int baseValue2;
public DecoratorBase()
{
baseValue1 = (new Random()).Next();
baseValue2 = (new Random()).Next();
}
protected void ShowBaseValue()
{
Console.WriteLine($"DecoratorBase - baseValue1: {baseValue1}, baseValue2: {baseValue2}");
}
}
[ProtoContract]
public class DecoratorDerived : DecoratorBase
{
[ProtoMember(2)]
public int derivedValue1;
private int derivedValue2;
public DecoratorDerived()
{
derivedValue1 = (new Random()).Next();
derivedValue2 = (new Random()).Next();
}
public void ShowValues()
{
ShowBaseValue();
Console.WriteLine($"DecoratorDerived - derivedValue1: {derivedValue1}, derivedValue2: {derivedValue2}");
}
}
static void DecoratorTest()
{
var c1 = new DecoratorDerived();
c1.ShowValues();
byte[] raw;
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<DecoratorDerived>(stream, c1);
raw = stream.ToArray();
}
DecoratorDerived c2;
using (var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<DecoratorDerived>(stream);
}
c2.ShowValues();
}
Все работает нормально, но проблема в том, что мои классы, полученные из базы, генерируются автоматически через T4, и их много, поэтому добавление всех строк ProtoInclude
нереально. Проведение некоторых исследований показало, что все работает на ходу с RuntimeTypeModel. Таким образом, изменение теста на
[ProtoContract]
public class RTMBase
{
[ProtoMember(2)]
private int baseValue1;
private int baseValue2;
public RTMBase()
{
baseValue1 = (new Random()).Next();
baseValue2 = (new Random()).Next();
}
protected void ShowBaseValue()
{
Console.WriteLine($"RTMBase - baseValue1: {baseValue1}, baseValue2: {baseValue2}");
}
}
[ProtoContract]
public class RTMDerived : RTMBase
{
[ProtoMember(2)]
public int derivedValue1;
private int derivedValue2;
public RTMDerived()
{
derivedValue1 = (new Random()).Next();
derivedValue2 = (new Random()).Next();
}
public void ShowValues()
{
ShowBaseValue();
Console.WriteLine($"RTMDerived - derivedValue1: {derivedValue1}, derivedValue2: {derivedValue2}");
}
}
static void RTMTest(RuntimeTypeModel runtimeTypeModel)
{
var c1 = new RTMDerived();
c1.ShowValues();
// setup RTM, https://stackoverflow.com/questions/40608767/inheritance-in-protobuf-net-adding-a-lower-base-class-still-backward-compatible
var myType = runtimeTypeModel[typeof(RTMDerived)];
var baseType = runtimeTypeModel[typeof(RTMDerived).BaseType];
if (!baseType.GetSubtypes().Any(s => s.DerivedType == myType))
{
foreach (var field in baseType.GetFields())
{
myType.Add(field.FieldNumber + 500, field.Name);
}
}
byte[] raw;
using(var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<RTMDerived>(stream, c1);
raw = stream.ToArray();
}
RTMDerived c2;
using(var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<RTMDerived>(stream);
}
c2.ShowValues();
}
Я получаю исключение
RTMBase - baseValue1: 1874947795, baseValue2: 1391655165
RTMDerived - derivedValue1: 922997568, derivedValue2: 837049520
Unhandled Exception: System.ArgumentException: Unable to determine member: baseValue1
Parameter name: memberName
at ProtoBuf.Meta.MetaType.AddField(Int32 fieldNumber, String memberName, Type itemType, Type defaultType, Object defaultValue) in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 1437
at ProtoBuf.Meta.MetaType.Add(Int32 fieldNumber, String memberName) in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 1261
at proto_error.Program.RTMTest1(RuntimeTypeModel runtimeTypeModel) in /Users/christian/tmp/proto-error/Program.cs:line 52
at proto_error.Program.Main(String[] args) in /Users/christian/tmp/proto-error/Program.cs:line 17
К счастью для меня, изменение RTMBase.baseValue1
на общедоступное, защищенное или внутреннее заставляет его работать, а внутреннее вполне подойдет для моего случая использования. Но мне любопытно, если это ошибка или я что-то не так делаю?
ДОПОЛНЕНИЕ
Если я изменю RMTest на
static void RTMTest2(RuntimeTypeModel runtimeTypeModel)
{
var c1 = new RTMDerived();
c1.ShowValues();
// setup RTM, https://stackoverflow.com/questions/10400539/protobuf-net-runtimetypemodel-not-serializing-members-of-base-class
var baseType = runtimeTypeModel[typeof(RTMDerived).BaseType];
baseType.AddSubType(500, typeof(RTMDerived));
byte[] raw;
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize<RTMDerived>(stream, c1);
raw = stream.ToArray();
}
RTMDerived c2;
using (var stream = new MemoryStream(raw))
{
c2 = ProtoBuf.Serializer.Deserialize<RTMDerived>(stream);
}
c2.ShowValues();
}
Работает просто отлично, но мне все еще любопытно, почему RTMRest1
терпит неудачу. Кроме того, параметр 500 должен быть различным для каждого типа или постоянным?