映射和序列化双向关联
本文关键字:关联 序列化 映射 | 更新日期: 2023-09-27 18:11:09
我以为这是一个微不足道的问题,马上就能找到答案,但不知何故,互联网证明了我的假设是错误的。
我有一个包含多个实体的模型,这些实体之间具有所有可能的关联(1对1、1对多、多对多、聚合、组合等)。为了简单起见,让我们举个例子。我们有Person类和Car类。一个人可以拥有多辆车,但一辆车只能属于一个人(所以是1对多关系)。现在,在Person中创建List/ArrayList来跟踪他/她的汽车将非常简单。不过,我也想了解一下车主的情况。所以,类看起来像这样:
[Serializable]
public class Person
{
[XmlElement]
public List<Car> Cars { get; set; }
[XmlAttribute]
public string Name { get; set; }
public Person()
{
Cars = new List<Car>();
}
}
[Serializable]
public class Car
{
[XmlElement]
public Person Owner { get; set; }
[XmlAttribute]
public string Type { get; set; }
public Car()
{
}
}
但是,我认为这种结构会导致Xml文件中的无限循环,如下所示:
<Person name="John Doe">
<Cars>
<Car type="Ford">
<Owner>
<Person name="John Doe">
<Cars>
<Car type="Ford">
<Owner>
<Person name="John Doe">
... etc.
我甚至尝试过,在序列化期间,我得到了"您需要将XmlChoiceIdentifierAttribute添加到'Owner'成员"异常。
我有几个问题:1. 有没有一种方法可以防止序列化中的循环?2. 如果没有,我是否必须为每个类编写自己的序列化器?3.这个映射可以吗?还是有其他更好的方法?我考虑过一个中央"映射器"类,它将根据ID返回所需的对象……但话又说回来,这可以通过SQL来完成。我想避免SQL(因为保持应用程序轻量级)。
XmlSerializer
是树序列化器,而不是图序列化器。最好的办法是在序列化过程中避免向后导航,例如:
[XmlIgnore]
public Person Owner { get; set; }
("parent"answers"owner"几乎都是反向导航)
不幸的是,XmlSerializer
不支持序列化后回调,否则可以添加如下内容:
[WhateverOnAfterDeserialized]
public void OnAfterDeserialized(...) {
foreach(var car in cars) car.Owner = this;
}
其他一些序列化器确实支持序列化回调,但是由于这个原因,这些其他序列化器也可能支持全图序列化。例如,DataContractSerializer
既可以支持回调,也可以支持完整的图形,但它对xml提供的控制要少得多。
另一个选择是有一个自定义集合类型,在添加/删除时维护父属性;例如:
public class Person
{
private readonly CarCollection cars;
public Person() {
cars = new CarCollection(this);
}
[XmlElement]
public CarCollection Cars { get { return cars; } }
...
}
with (in CarCollection
):
// add code...
innerList.Add(value);
value.Parent = parent; // this is the field stored in the constructor
就我个人而言,我认为这可能太过分了。
另一种选择是简单地添加一个在反序列化之后调用的修复方法,它可以做任何必要的事情(并向下级联):
public void FixupAfterDeserializer() {
foreach(var car in cars) car.Parent = this;
}
请注意,您必须手动调用它。
最后,注意XmlSerializer
不需要[Serializable]