Насколько я знаю, protobuf-net, начиная с v2, поддерживает ссылки, но их нельзя использовать вместе с суррогатом (исключение "В этом случае выбрасывается ссылка, отслеживаемая ссылками, объект изменил ссылку во время десериализации" ))
Мне интересно, есть ли какой-нибудь обходной путь, который я не рассматривал, чтобы заставить его работать.
Здесь следуем коду моего теста, который воспроизводит вышеупомянутое исключение.
Классы
public class Person
{
public Person(string name, GenderType gender)
{
Name = name;
Gender = gender;
}
public string Name { get; set; }
public GenderType Gender { get; set; }
}
[Flags]
public enum GenderType : byte
{
Male = 1,
Female = 2,
Both = Male | Female
}
public class Family
{
public Family(List<Person> people, Person familyHead = null)
{
People = people;
FamilyHead = familyHead;
}
public List<Person> People { get; set; }
public Person FamilyHead { get; set; }
}
public class PersonSurrogate
{
public string Name { get; set; }
public byte Gender { get; set; }
public PersonSurrogate(string name, byte gender)
{
Name = name;
Gender = gender;
}
#region Static Methods
public static implicit operator Person(PersonSurrogate surrogate)
{
if (surrogate == null) return null;
return new Person(surrogate.Name, (GenderType)surrogate.Gender);
}
public static implicit operator PersonSurrogate(Person source)
{
return source == null ? null : new PersonSurrogate(source.Name, (byte)source.Gender);
}
#endregion
}
public class FamilySurrogate
{
public FamilySurrogate(List<Person> people, Person familyHead)
{
People = people;
FamilyHead = familyHead;
}
public List<Person> People { get; set; }
public Person FamilyHead { get; set; }
#region Static Methods
public static implicit operator Family(FamilySurrogate surrogate)
{
if (surrogate == null) return null;
return new Family(surrogate.People, surrogate.FamilyHead);
}
public static implicit operator FamilySurrogate(Family source)
{
return source == null ? null : new FamilySurrogate(source.People, source.FamilyHead);
}
#endregion
}
Сериализатор
/// <summary>
/// Class with model for protobuf serialization
/// </summary>
public class FamilySerializer
{
public GenderType GenderToInclude;
public FamilySerializer(Family family, GenderType genderToInclude = GenderType.Both)
{
GenderToInclude = genderToInclude;
Family = family;
Init();
}
private void Init()
{
Model = RuntimeTypeModel.Create();
FillModel();
Model.CompileInPlace();
}
public FamilySerializer()
{
Init();
}
public Family Family { get; set; }
public RuntimeTypeModel Model { get; protected set; }
protected virtual void FillModel()
{
Model = RuntimeTypeModel.Create();
Model.Add(typeof(Family), false)
.SetSurrogate(typeof(FamilySurrogate));
MetaType mt = Model[typeof(FamilySurrogate)];
mt.Add(1, "People");
mt.AddField(2, "FamilyHead").AsReference = true; // Exception "A reference-tracked object changed reference during deserialization" - because using surrogate.
mt.UseConstructor = false;
Model.Add(typeof(Person), false)
.SetSurrogate(typeof(PersonSurrogate));
mt = Model[typeof(PersonSurrogate)]
.Add(1, "Name")
.Add(2, "Gender");
mt.UseConstructor = false; // Avoids to use the parameterless constructor.
}
public void Save(string fileName)
{
using (Stream s = File.Open(fileName, FileMode.Create, FileAccess.Write))
{
Model.Serialize(s, Family, new ProtoBuf.SerializationContext(){Context = this});
}
}
public void Open(string fileName)
{
using (Stream s = File.Open(fileName, FileMode.Open, FileAccess.Read))
{
Family = (Family)Model.Deserialize(s, null, typeof(Family), new ProtoBuf.SerializationContext(){Context = this});
}
}
}
Контрольный пример
private Family FamilyTestCase(string fileName, bool save)
{
if (save)
{
var people = new List<Person>()
{
new Person("Angus", GenderType.Male),
new Person("John", GenderType.Male),
new Person("Katrina", GenderType.Female),
};
var fam = new Family(people, people[0]);
var famSer = new FamilySerializer(fam);
famSer.Save(fileName);
return fam;
}
else
{
var famSer = new FamilySerializer();
famSer.Open(fileName);
if (Object.ReferenceEquals(fam.People[0], fam.FamilyHead))
{
// I'd like this condition would be satisfied
}
return famSer.Family;
}
}