Я не хочу путать наши BusinessObjects с какой-либо логикой сериализации.Вот почему я хотел внедрить Surrogate для каждого BusinessObject, который инкапсулирует Данные, необходимые для сериализации и десериализации BusinessObject.Кроме того, он содержит всю бизнес-логику, которая должна выполняться после сериализации.
Однако в словарях, похоже, есть проблема с отслеживанием ссылок.Рассмотрим следующий пример:
//DataContracts:
public class SerializeClass
{
public Dictionary<string, SerializeDictionaryItem> MyDictionary { get; set; }
public List<SerializeDictionaryItem> MyList { get; set; }
}
[ProtoContract]
public class SerializeDictionaryItem
{
[ProtoMember(1)]
public string MyField { get; set; }
}
[ProtoContract(SkipConstructor = true)]
public class SerializeClassSurrogate
{
[ProtoMember(1000, AsReference = true)]
public Dictionary<string, SerializeDictionaryItem> MyDictionary { get; set; }
[ProtoMember(1001, AsReference = true)]
public List<SerializeDictionaryItem> MyList { get; set; }
public static implicit operator SerializeClass(SerializeClassSurrogate surrogate)
{
if (surrogate == null)
return null;
var serializeClass = new SerializeClass();
serializeClass.MyDictionary = surrogate.MyDictionary;
serializeClass.MyList = surrogate.MyList;
return serializeClass;
}
public static implicit operator SerializeClassSurrogate(SerializeClass serializeClass)
{
if (serializeClass == null)
return null;
var surrogate = new SerializeClassSurrogate();
surrogate.MyDictionary = serializeClass.MyDictionary;
surrogate.MyList = serializeClass.MyList;
return surrogate;
}
}
//Serialization Logic:
RuntimeTypeModel.Default[typeof(SerializeClass)].SetSurrogate(typeof(Surrogates.SerializeClassSurrogate));
var myDictionaryItem = new SerializeDictionaryItem();
myDictionaryItem.MyField = "ABC";
SerializeClass m = new SerializeClass() {MyDictionary = new Dictionary<string, SerializeDictionaryItem>()};
m.MyDictionary.Add("def", myDictionaryItem);
m.MyDictionary.Add("abc", myDictionaryItem);
m.MyList = new List<SerializeDictionaryItem>();
m.MyList.Add(myDictionaryItem);
m.MyList.Add(myDictionaryItem);
using (var writer = new StreamWriter(OutputDir + "proto.bin"))
{
Serializer.Serialize(writer.BaseStream, m);
}
using(var reader = new StreamReader(OutputDir + "proto.bin"))
{
var deserialized = Serializer.Deserialize<SerializeClass>(reader.BaseStream);
var areEqualInDictionary = deserialized.MyDictionary["def"] == deserialized.MyDictionary["abc"];
var areEqualInList = deserialized.MyList[0] == deserialized.MyList[1];
}
Два логических значения areEqualIn ... должны быть истинными после десериализации.Однако я получаю следующее исключение во время десериализации:
"Объект, отслеживаемый ссылками, изменил ссылку во время десериализации"
Это ошибка или я что-то не так делаю?
/РЕДАКТИРОВАТЬ: Это, похоже, не имеет ничего общего с использованием суррогатов.Если я попробую сериализацию без них и просто заменим SerializeClass на ProtoContract и его членов соответственно на ProtoMembers, то возникнет точно такая же проблема.
Кстати.Я использую protobuf-net v2 429
/ EDIT2: Это исключение также можно спровоцировать с помощью этого кода, который вообще не нуждается в словарях:
public class SerializeClass
{
public string MyField { get; set; }
public SerializeClass GroupTrailerRef { get; set; }
}
[ProtoContract(SkipConstructor = true)]
public class SerializeClassSurrogate
{
[ProtoMember(1001)]
public string MyField { get; set; }
[ProtoMember(1002, AsReference = true)]
public SerializeClass GroupTrailerRef { get; set; }
public static implicit operator SerializeClass(SerializeClassSurrogate surrogate)
{
if (surrogate == null)
return null;
var serializeClass = new SerializeClass();
serializeClass.MyField = surrogate.MyField;
serializeClass.GroupTrailerRef = surrogate.GroupTrailerRef;
return serializeClass;
}
public static implicit operator SerializeClassSurrogate(SerializeClass serializeClass)
{
if (serializeClass == null)
return null;
var surrogate = new SerializeClassSurrogate();
surrogate.MyField = serializeClass.MyField;
surrogate.GroupTrailerRef = serializeClass.GroupTrailerRef;
return surrogate;
}
}
RuntimeTypeModel.Default[typeof(SerializeClass)].SetSurrogate(typeof(Surrogates.SerializeClassSurrogate));
//Serialization Code:
SerializeClass groupTrailer = new SerializeClass();
groupTrailer.MyField = "Group";
SerializeClass m = new SerializeClass() {};
m.GroupTrailerRef = groupTrailer;
m.MyField = "First";
SerializeClass k = new SerializeClass();
k.GroupTrailerRef = groupTrailer;
k.MyField = "Second";
var lst = new List<SerializeClass>();
lst.Add(m);
lst.Add(k);
using (var writer = new StreamWriter(OutputDir + "proto.bin"))
{
Serializer.Serialize(writer.BaseStream, lst);
}
using(var reader = new StreamReader(OutputDir + "proto.bin"))
{
var deserialized = Serializer.Deserialize<List<SerializeClass>>(reader.BaseStream);
var areEqual = deserialized[0].GroupTrailerRef == deserialized[1].GroupTrailerRef;
}
Что вы предлагаете?Мне нравится суррогатный метод для отделения логики сериализации от моих бизнес-объектов.Как вы думаете, я должен просто придерживаться "нормального" пути?Однако в этом случае я снова застрял из-за проблемы 203 ...
Спасибо, TH