c# - 带有代理项的 Protobuf-net 对象图参考
问题描述
据我所知,从 v2 开始的 protobuf-net 支持引用,但它们不能与代理一起使用(在这种情况下会抛出异常“反序列化期间引用跟踪的对象更改引用” )
我想知道是否有一些我没有考虑过使其工作的解决方法。
下面是重现上述异常的我的测试用例的代码。
课程
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;
}
}
解决方案
我认为现在这只是一个不受支持的场景,我不知道有什么方法可以让它神奇地工作;这可能是我可以在某个时候回到的东西,但是有许多更高优先级的事情会优先考虑。
我在这里通常的建议 - 这适用于任何序列化程序,而不仅仅是 protobuf-net:任何时候你发现自己遇到了序列化程序的限制,或者甚至只是在序列化程序中配置一些尴尬的东西:停止与序列化程序作斗争。当人们尝试序列化他们的常规域模型时,几乎总是会出现这种问题,而域模型中的某些内容并不适合他们选择的序列化器。不要尝试神秘的魔法:拆分你的模型——让你的域模型非常适合你希望你的应用程序看到的内容,并创建一个单独的模型非常适合您的序列化程序的模型。那么你就不需要像“代理”这样的概念了。如果您使用多种序列化格式,或者在同一序列化格式中有多个不同“版本”的布局:拥有多个序列化模型。
尝试在模型上为多个主人服务真的不值得头疼。
推荐阅读
- python - Json 值打印 TypeError:字符串索引必须是整数,而不是 str
- python - 正则表达式 - 使用正则表达式从 python 列表中删除日期和时间
- android - 未知的 android 属性 SherlockSpinner
- python - InvalidArgumentError: input_1:0 已输入和提取
- python - 绕过 cookiewall 硒
- python - nltk如何给出多个分隔的句子
- ruby-on-rails - NGINX反向代理后的cookies
- xcode - 由于未捕获的异常“NSUnknownKeyException”而终止应用程序,原因:setValue:forUndefinedKey, _requiresUserActionForMediaPlayback
- excel-formula - 将第 1 列和第 2 列与另一个表进行比较
- java - 如何使用 c:foreach 解决这个数据库读取问题?(如果它与此有关..)