views:

328

answers:

2

When trying to replace the content of an XML file in C#.NET with a snippet like this:

string file = Path.GetTempFileName(); // pretend this is a real file
string tmpFile = Path.GetTempFileName();

using (var writer = XmlWriter.Create(File.Create(tmpFile)))
{
    writer.WriteStartElement("root");
    for (int i = 0; i < 100; i++)
    {
        writer.WriteElementString("test", null, 
            "All work and no play makes Jack a dull boy");
    }
    writer.WriteEndElement();
}

File.Delete(file);
File.Move(tmpFile, file);

... I get a System.IO.IOException claiming that the file is already opened by another process.

+5  A: 

For some reason the XmlWriter class evidently does not dispose the underlying stream for the temporary file. Putting the stream in a "using" clause of its own makes sure the stream is closed correctly. Changing the code to

string file = Path.GetTempFileName(); // pretend this is a real file
string tmpFile = Path.GetTempFileName();

using (var stream = File.Create(tmpFile))
using (var writer = XmlWriter.Create(stream))
{
    writer.WriteStartElement("root");
    for (int i = 0; i < 100; i++)
    {
        writer.WriteElementString("test", null, 
            "All work and no play makes Jack a dull boy");
    }
    writer.WriteEndElement();
}                
File.Delete(file);
File.Move(tmpFile,file);

... makes the IOException disappear and it works as intended.

Christoffer Soop
Updated the answer with chained using statements as suggested by Greg D
Christoffer Soop
+4  A: 

Using statements can be chained. Slightly modifying your code:

string file = Path.GetTempFileName(); // pretend this is a real file
string tmpFile = Path.GetTempFileName();

using (var stream = File.Create(tmpFile))
using (var writer = XmlWriter.Create(stream))
{
    writer.WriteStartElement("root");
    for (int i = 0; i < 100; i++)
    {
        writer.WriteElementString("test", null, 
            "All work and no play makes Jack a dull boy");
    }
    writer.WriteEndElement();
}                
File.Delete(file);
File.Move(tmpFile,file);

If you're dealing with multiple disposable entities over the same scope (not an uncommon occurrence), this avoids nastily deep nesting. :)

Greg D
Cool, I did not know that... I guess it is safe to assume that the using statments are closed in reverse order, i.e. the last disposable object is disposed of first?
Christoffer Soop
Actually it's exactly the same thing as doing `using(A a = new A()) { using(B = new B()) { (do stuff) } }`. The outer `using` statement has exactly one statement inside it (namely the inner `using` statement).
Yuliy