如何确保child.ParentId
在反序列化后总是等于parent.Id
?
在反序列化之后设置Step.ParentId
的自然方法是在OnDeserialized
事件中这样做。不幸的是,XmlSerializer
does not support deserialization events。鉴于此,您可能需要调查其他设计。
一种可能性是,以取代List<Step>
使用自定义集合自动保持当一个孩子被添加到父,沿Maintaining xml hierarchy (ie parent-child) information in objects generated by XmlSerializer线的ParentId
参考。遗憾的是,ObservableCollection
不适用于此目的,因为the list of old items is not included in the notification event when it is cleared。但是,通过继承System.Collections.ObjectModel.Collection<T>
来制造我们自己的产品非常容易。
因此,您的对象模型将成为以下内容。请注意,我已经修改了你的一些属性名称遵循c# naming guidelines:
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Steps
{
readonly ChildCollection<Step> steps;
public Steps()
{
this.steps = new ChildCollection<Step>();
this.steps.ChildAdded += (s, e) =>
{
if (e.Item != null)
e.Item.ParentId = null;
};
}
[System.Xml.Serialization.XmlElementAttribute("Step")]
public Collection<Step> StepList { get { return steps; } }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Step
{
readonly ChildCollection<Step> steps;
public Step()
{
this.steps = new ChildCollection<Step>();
this.steps.ChildAdded += (s, e) =>
{
if (e.Item != null)
e.Item.ParentId = this.Id;
};
}
[System.Xml.Serialization.XmlElementAttribute("Step")]
public Collection<Step> StepList { get { return steps; } }
[System.Xml.Serialization.XmlAttributeAttribute("Name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute("id")]
public string Id { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute("ParentID")]
public string ParentId { get; set; }
}
public class ChildCollectionEventArgs<TChild> : EventArgs
{
public readonly TChild Item;
public ChildCollectionEventArgs(TChild item)
{
this.Item = item;
}
}
public class ChildCollection<TChild> : Collection<TChild>
{
public event EventHandler<ChildCollectionEventArgs<TChild>> ChildAdded;
public event EventHandler<ChildCollectionEventArgs<TChild>> ChildRemoved;
void OnRemoved(TChild item)
{
var removed = ChildRemoved;
if (removed != null)
removed(this, new ChildCollectionEventArgs<TChild>(item));
}
void OnAdded(TChild item)
{
var added = ChildAdded;
if (added != null)
added(this, new ChildCollectionEventArgs<TChild>(item));
}
public ChildCollection() : base() { }
protected override void ClearItems()
{
foreach (var item in this)
OnRemoved(item);
base.ClearItems();
}
protected override void InsertItem(int index, TChild item)
{
OnAdded(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
if (index >= 0 && index < Count)
{
OnRemoved(this[index]);
}
base.RemoveItem(index);
}
protected override void SetItem(int index, TChild item)
{
OnAdded(item);
base.SetItem(index, item);
}
}
现在ParentId
将每当孩子被添加到父设置,既deserialzation后,在任何应用程序代码。
(如果因任何原因不能与Collection<Step>
取代你List<Step>
,你可以考虑序列化数组代理属性的设置器的ParentId
值,沿XML deserialization with parent object reference行。但我是不由自主地想到一个设计将在所有情况下的父ID是优选的。)
我怎样才能通过指定ParentId
添加Step
到Step
对象树?
您可以创建递归Linq
扩展横穿Step
层次,沿Efficient graph traversal with LINQ - eliminating recursion行:
public static class StepExtensions
{
public static IEnumerable<Step> TraverseSteps(this Steps root)
{
if (root == null)
throw new ArgumentNullException();
return RecursiveEnumerableExtensions.Traverse(root.StepList, s => s.StepList);
}
public static IEnumerable<Step> TraverseSteps(this Step root)
{
if (root == null)
throw new ArgumentNullException();
return RecursiveEnumerableExtensions.Traverse(root, s => s.StepList);
}
public static bool TryAdd(this Steps root, Step step, string parentId)
{
foreach (var item in root.TraverseSteps())
if (item != null && item.Id == parentId)
{
item.StepList.Add(step);
return true;
}
return false;
}
public static void Add(this Steps root, Step step, string parentId)
{
if (!root.TryAdd(step, parentId))
throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
}
}
public static class RecursiveEnumerableExtensions
{
// Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
// to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
// to ensure items are returned in the order they are encountered.
public static IEnumerable<T> Traverse<T>(
T root,
Func<T, IEnumerable<T>> children)
{
yield return root;
var stack = new Stack<IEnumerator<T>>();
try
{
stack.Push((children(root) ?? Enumerable.Empty<T>()).GetEnumerator());
while (stack.Count != 0)
{
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
{
stack.Pop();
enumerator.Dispose();
}
else
{
yield return enumerator.Current;
stack.Push((children(enumerator.Current) ?? Enumerable.Empty<T>()).GetEnumerator());
}
}
}
finally
{
foreach (var enumerator in stack)
enumerator.Dispose();
}
}
public static IEnumerable<T> Traverse<T>(
IEnumerable<T> roots,
Func<T, IEnumerable<T>> children)
{
return from root in roots
from item in Traverse(root, children)
select item;
}
}
他们的孩子加入到由ID特定的父母,你会怎么做:
steps.Add(new Step { Id = "4C", Name = "S112C" }, "4");
原型fiddle。
更新
如果您不知何故无法新增extension methods到Step
和Steps
,因为它们是嵌套类,你可以添加TraverseSteps()
和Add()
为对象的方法:
public partial class Step
{
public IEnumerable<Step> TraverseSteps()
{
return RecursiveEnumerableExtensions.Traverse(this, s => s.StepList);
}
}
public partial class Steps
{
public IEnumerable<Step> TraverseSteps()
{
return RecursiveEnumerableExtensions.Traverse(StepList, s => s.StepList);
}
public bool TryAdd(Step step, string parentId)
{
foreach (var item in TraverseSteps())
if (item != null && item.Id == parentId)
{
item.StepList.Add(step);
return true;
}
return false;
}
public void Add(Step step, string parentId)
{
if (!TryAdd(step, parentId))
throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
}
}
@dbc非常感谢为您的评论和答案!在第二个问题中,我的意思是通过搜索给定的Step类来向Step层次结构中插入Step。 – Royeh