I was surprised to see that not all of my elements in the following code are being iterated:
IEnumerable<XElement> dataStorageGroupElements = document.Descendants().Where(x => "Xms.Common.DataStorageGroup" == (string)x.Attribute("NodeType"));
int counter = 0;
foreach (XElement dataStorageGroupElement in dataStorageGroupElements)
{
Console.WriteLine($"Counter={counter++}, NbElements={dataStorageGroupElements.Count()}");
XElement newParent = GetNewParentForDataStorageGroup(dataStorageGroupElement);
dataStorageGroupElement.Remove();
newParent.Add(dataStorageGroupElement);
}
Output is:
Counter=0, NbElements=12
Counter=1, NbElements=12
Counter=2, NbElements=12
Counter=3, NbElements=12
Counter=4, NbElements=12
Counter=5, NbElements=12
Counter=6, NbElements=12
Changing the code to execute enumeration with ToArray() fixes this issue:
IEnumerable<XElement> dataStorageGroupElements = document.Descendants().Where(x => "Xms.Common.DataStorageGroup" == (string)x.Attribute("NodeType")).ToArray();
With the above code the counter is being increment to 11.
Why some elements are being skipped is described here. But why is NbElements always 12? Is it only evaluated the first time?
The problem is described in MSDN: "Mixed declarative code / imperative code bugs (LINQ to XML)":
LINQ to XML contains various methods that allow you to modify an XML tree directly. You can add elements, delete elements, change the contents of an element, add attributes, and so on. This programming interface is described in Modifying XML Trees (LINQ to XML) (C#). If you are iterating through one of the axes, such as Elements, and you are modifying the XML tree as you iterate through the axis, you can end up with some strange bugs.
This problem is sometimes known as "The Halloween Problem".
Basically, modifying something while you're lazily iterating over it is a bad idea. Your approach of using ToArray
to materialize the query before you start modifying things is the right one, although I'd personally use ToList
instead of ToArray
.
See more on this question at Stackoverflow