0

I'm using XDocument to load and save a config file. Most of the time it works correctly, but there are cases (seemingly random) where it appends extra information after the last tag. Here's a gist of what's happening.

Config file pre-run:

<?xml version="1.0" encoding="us-ascii"?>
<local>
</local>

code:

XDocument config = new XDocument();
config = XDocument.Load(new FileStream(@"c:\foo.xml", FileMode.Open, FileAccess.Read));
XElement fileEle = config.Root.Element("files");
XElement statsEle = new XElement("stats");
statsEle.Add(new XElement("one", "two"));
statsEle.Add(new XElement("three", "four"));
.
.
.
fileEle.Add(statsEle);
config.Save(new FileStream(@"c:\foo.xml", FileMode.Create, FileAccess.Write), SaveOptions.None);

config file post-run:

<?xml version="1.0" encoding="us-ascii"?>
<local>
    <files>
        <one>two</one>
        <three>four</three>
    </files>
</local>s>
</local>

Any suggestions? No idea why the extra characters are being added. Sometimes it's the extra tag, sometimes it's different characters and sometimes it works correctly. I've tried loading/saving using different methods (XMLReader, etc.), adding XML tags different ways.. after X runs they all produce the same error. Thanks for the help!

Eckstein
  • 183
  • 5

3 Answers3

5

You have two FileStream's open for the same file concurrently... one for reading foo.xml, and one for overwriting it. That seems very problematic.

I'd recommend:

  • Disposing of the read FileStream as soon as your XmlDocument is loaded:
using (FileStream fs = new FileStream(@"C:\foo.xml", FileMode.Open, FileAccess.Read))
{
    config.Load(fs);
}
  • Use FileMode.Truncate in your write FileStream so that the file is truncated to 0 bytes before you start writing to it.
Andrew Brown
  • 4,086
  • 1
  • 24
  • 21
  • Good catch. The `Xdocument.Load(new FileStream)...))` leaves a dangling File open. – H H Mar 17 '11 at 17:16
  • @Andrew good catch. I was still getting errors after closing the filestreams, but it seems that opening with truncate fixed the problem. Thanks! – Eckstein Mar 17 '11 at 17:36
  • @Eckstein: If this solution works, then using `XDocument.Load` and `config.Save(filename)` should work too. – Jon Skeet Mar 17 '11 at 17:43
  • That's what I figured.. seems should be same implementation under the hood. – Eckstein Mar 17 '11 at 17:58
  • After some more testing Load and Save worked. DLL may have been out of date.. Thanks Jon. – Eckstein Mar 17 '11 at 18:05
1

My guess is that something's actually overwriting (but not truncating) the file with fewer tags - and what you're seeing is the remains of a larger XML file.

I've never seen LINQ to XML do anything like this itself.

Can you run the same tests which always fail, but in a mode where each iteration writes to a new file instead of overwriting the old one?

Is there any reason you're loading directly from FileStreams (which are never being closed, as another answer points out) instead of just using XDocument.Load(filename), XDocument.Save(filename)?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • that's exactly right. I battled with this for some time a while back – Andrew Mar 17 '11 at 17:05
  • still experienced with .Save, but writing to a new file fixed the problem. Seems like a flawed design that the original file isn't deleted, or something similar, prior to writing. Or maybe I just have bad luck :) Thanks! – Eckstein Mar 17 '11 at 17:38
  • @Eckstein: Are you sure you don't have multiple threads doing this? Is this writing to a network share, or just a local drive? – Jon Skeet Mar 17 '11 at 17:42
  • It's a multi-threaded application, but no more than one thread is ever accessing the config file. Writing to a local drive. – Eckstein Mar 17 '11 at 17:53
0

I've seen this before... just can't think how to resolve it. I think it's got something to do with the way you are saving. You need to make sure you overwrite everything. I can't remember if it's the filemode or options... or another setting

Andrew
  • 5,215
  • 1
  • 23
  • 42