如何確保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