Reputation: 41
I intend to load an xml file using XDocument.Load(), update values of some elements, then save it using XDocument.Save(). Reading works fine, saving just won't work.
My code:
class XmlDocHandler
{
private string filePath;
private XDocument xmlDoc;
private IList<XElement> updatedElements;
public IEnumerable<XElement> Elements => xmlDoc.Descendants();
public IEnumerable<XElement> UpdatedElements => updatedElements;
public XmlDocHandler(string filePath)
{
this.filePath = filePath;
ReloadFromFile();
updatedElements = new List<XElement>();
}
public void UpdateElements(IEnumerable<XElement> newElements)
{
updatedElements = new List<XElement>();
foreach (XElement newElement in newElements)
{
XElement element = xmlDoc.Descendants()
.FirstOrDefault(x => x.Name.LocalName == newElement.Name.LocalName);
if (element != null)
{
if (element.Value != newElement.Value)
{
element.Value = newElement.Value;
updatedElements.Add(element);
}
}
}
}
public void ReloadFromFile()
{
bool success = false;
if (File.Exists(filePath))
{
try
{
xmlDoc = XDocument.Load(filePath);
success = true;
}
catch
{
}
}
if (!success)
{
xmlDoc = new XDocument();
}
}
public void WriteToFile()
{
xmlDoc.Save(filePath);
}
}
As far as I can tell, its a serialized set of operations, nothing parallel or other fancy stuff, that could block my file. I've found no indication that XDocument.Load("c:\file.xml") would create a lock on the file.
I've tried to replace the straight forward operations
xmlDoc = XDocument.Load(filePath);
and
xmlDoc.Save(filePath);
with the stream based approaches found here: XDocument.Save() unable to access file and here c# xml.Load() locking file on disk causing errors so that they look like this:
Loading..
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
xmlDoc = XDocument.Load(fs);
}
or
using (var sr = new StreamReader(filePath))
{
xmlDoc = XDocument.Load(sr);
}
and writing..
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.Write))
{
xmlDoc.Save(fs);
}
No matter what I do, closing the streams properly and making sure the file isn't opened in any editor or otherwise used, the stupid file is always "used by another process".
What exactly am I not seeing here? The file in question resides in my Debug output folder of VS2017 Pro next to the .exe file. I'm not aware that I have limited write access in that folder.
Upvotes: 2
Views: 1660
Reputation: 41
I found the error causing this fiasco!
The reason for this issue has nothing to do with XDocument.Load()
or .Save()
. While being relieved to have solved my problem, I'm also embarrassed to admit that one of my own implementaions I've made years ago caused this.
I've once made a wrapper class for System.Windows.Forms.OpenFileDialog()
that would ease my frequently used configuration of the OFD class. In it I've used something like
var ofd = new OpenFileDialog();
try
{
if((var stream = ofd.OpenFile()) != null)
{
return ofd.FileName;
}
else
{
return "file already opened";
}
}
catch
{
return "error";
}
while keeping the ofd
and the stream
references as instance variables. Looking at this code now makes me shiver (or laugh) but back then I didn't know any better. Of course this has now bitten me in my a.., because I did not close that stream! A crime I've committed years ago now cost me almost 2 days of work.
My remedy: I've rewritten my OFD wrapper class to use the Microsoft.Win32.OpenFileDialog
class, because that does not seem to have any stream based stuff that would lock files unintentionally.
Lesson(s) learned (and what I warmly recommend to others): never trust yourself when using streams or other constructs that can be left open and cause memory leaks. Read up on the using block statement that makes use of the dispose pattern by calling the IDisposable.Dispose()
method which usually takes care of such cleanup work. And lastly, use the neat profiling features in VS that weren't available back when I made my OFD wrapper, they help discover such issues.
Thanks to all who helped and to SO to make their help available.
Upvotes: 2
Reputation: 11
try
fs.Flush();
and maybe
fs.Close();
after writing. AFAIK there is some sort of caching going on in the IO Streams. It might help.
Regards.
Upvotes: 0