I have the following extension methods:
public static IEnumerable<XElement> GetElement(this XmlReader reader, string elementName)
{
reader.MoveToElement();
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element
&& reader.Name.Equals(elementName))
{
yield return XNode.ReadFrom(reader) as XElement;
}
reader.Read();
}
}
public static IEnumerable<XElement> ExtractElement(this Stream stream, string elementName)
{
using (var reader = XmlReader.Create(stream))
{
return reader.GetElement(elementName));
}
}
I then try to open a FileStream
and get a specific element in an XML
file using:
using (var stream = File.Open("My.xml", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var element = stream.ExtractElement("subscriptionForms").First();
element.Elements("subscriptionForm").Count().ShouldBe(11);
}
However running the code results in an infinite loop due to the xml reader getting closed (reader.EOF
being false
and reader.Read()
not doing anything) but when I change the following line:
return reader.GetElement(elementName));
to:
foreach (var xElement in reader.GetElement(elementName, ignoreCase))
{
yield return xElement;
}
everything seems to be working fine. Why is the former implementation causing the reader to close?
reader.GetElement(elementName)
does not start enumerating yet. It returns an object that, once you look at what it contains, starts reading from reader
. Since you return that object directly, and the return
statement is wrapped in a using
, the using
statement causes the reader to be closed before the enumeration starts.
When you wrap it in a foreach
loop, the using
statement's effect of closing the reader is delayed until the foreach
terminates, and by that time it'll be okay, by that time you no longer need the reader.