Using statement around XmlWriter causes premature closing of stream

1k Views Asked by At

I'm trying to write a small piece of XML to a memory stream, then read the written content from the memory stream and save it to a variable.

I have the following logic that works for writing and reading simple text value:

string text = null;

using (var memoryStream = new MemoryStream())
{
    using (var streamWriter = new StreamWriter(memoryStream))
    {
        streamWriter.Write("Foo!");
        streamWriter.Flush();

        memoryStream.Position = 0;

        using (var streamReader = new StreamReader(memoryStream))
        {
            text = streamReader.ReadToEnd();
        }
    }
}

await context.Response.WriteAsync(text, Encoding.UTF8);

But replacing the StreamWriter with XmlWriter and trying to write actual XML results in ObjectDisposedException.

string text = null;

using (var memoryStream = new MemoryStream())
{
    using (var xmlWriter = XmlWriter.Create(memoryStream))
    {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("Foo");
        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndDocument();
        xmlWriter.Flush();

        memoryStream.Position = 0;

        using (var streamReader = new StreamReader(memoryStream))
        {
            text = streamReader.ReadToEnd();
        }
    }
}
System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.Xml.XmlUtf8RawTextWriter.FlushBuffer()
   at System.Xml.XmlUtf8RawTextWriter.Flush()
   at System.Xml.XmlWellFormedWriter.Close()
   at System.Xml.XmlWriter.Dispose(Boolean disposing)
   at System.Xml.XmlWriter.Dispose()

But if I remove the using block around XmlWriter... it works.

string text = null;

using (var memoryStream = new MemoryStream())
{
    var xmlWriter = XmlWriter.Create(memoryStream));

    xmlWriter.WriteStartDocument();
    xmlWriter.WriteStartElement("Foo");
    xmlWriter.WriteEndElement();
    xmlWriter.WriteEndDocument();
    xmlWriter.Flush();

    memoryStream.Position = 0;

    using (var streamReader = new StreamReader(memoryStream))
    {
        text = streamReader.ReadToEnd();
    }
}

await context.Response.WriteAsync(text, Encoding.UTF8);

I cannot get it around my head why that happens and why I cannot use the using block around TextWriter. Same steps done with StreamWriter works just fine, so what's the difference with XmlWriter? I assume that stream will not be closed until the end of using block is reached. Where is the flaw in my way of thinking?

2

There are 2 best solutions below

0
On BEST ANSWER

Look at the stacktrace: the exception is happening when XmlWriter is disposed, at the end of its block. It is disposed, it flushes, it tries to write to the underlying stream and bang! - something has already disposed the underlying stream.

StreamReader's block ends just before XmlWriter's block, and this disposal also (by default) disposes the underlying stream. It's the only thing that could have done it.

The reason for it working in the other cases is that StreamWriter must not flush on dispose (or much more likely - it doesn't need to flush because its buffer is either empty or non-existing). This means that it doesn't matter that StreamReader has nipped in before it.

In the last case it works because XmlWriter isn't being disposed, and isn't trying to flush at all (but then, it evidently doesn't need to anyway, as you've already manually flushed, and the output is correct - I wonder why its dispose method insists on flushing then? but it is doing)

The general solution here is to use using more selectively. You can either miss it out completely for StreamReader, or you can pass the 'leaveOpen' parameter to it.

My usual approach is to only worry about disposing the stream, as it's always the stream that is linked to an unmanaged resource needing disposal. The disposing of readers and writers is really just a nice means of succinctly disposing the stream.

0
On

By default StreamReader disposes underlying stream. If you need it after StreamReader is disposed (and it is used by XmlWriter on dispose to perform Flush) you can to set leaveOpen ctor parameter to true:

using (var streamReader = new StreamReader(memoryStream, leaveOpen: true))