Search code examples
c#xmllinq-to-xmlworker-thread

Unable to write to XML file using threads and XDocument


string filepath = Environment.CurrentDirectory + @"Expense.xml";

public void  WriteToXML(object param)
{
Expense exp = (Expense)param;
   if (File.Exists(filepath)) {
    XDocument xDocument = XDocument.Load(filepath);

    XElement root = xDocument.Element("Expenses");
    IEnumerable<XElement> rows = root.Descendants("Expense");
    XElement firstRow = rows.First();

    firstRow.AddBeforeSelf(new XElement("Expense",
           new XElement("Id", exp.Id.ToString()),
           new XElement("Amount", exp.Amount.ToString()),
           new XElement("Contact", exp.Contact),
           new XElement("Description", exp.Description),
           new XElement("Datetime", exp.Datetime)));
    xDocument.Save(filepath);
 }
}

Expense exp = new Expense();
exp.Id = new Random().Next(1, 10000);
exp.Amount = float.Parse(text1[count].Text);
exp.Contact = combo1[count].SelectedItem.ToString();
exp.Description = rtext1[count].Text.ToString();
exp.Datetime = DateTime.Now.ToString("MM-dd-yyyy");

workerThread = new Thread(newParameterizedThreadStart(WriteToXML));
workerThread.Start(exp); // throws System.IO.IOException

I'm unable to write to XML file with worker treads - I'm getting this error:

System.IO.IOException: 'The process cannot access the file 'C:\work\FinanceManagement\FinanceManagement\bin\DebugExpense.xml' because it is being used by another process.

but if I use it like WriteToXML(exp); it works. I think XDocument.Load(filepath) is not thread safe. How can I resolve this issue?


Solution

  • Try introducting a lock, see if it resolves the issue:

    // Declare this somewhere in your project, can be in same class as WriteToXML
    static object XmlLocker;
    

    Then wrap a lock around the logic:

    public void WriteToXML(object param)
    {
        Expense exp = (Expense)param;
    
        lock (XmlLocker) // <-- this limits one thread at a time
        {
            if (File.Exists(filepath))
            {
                XDocument xDocument = XDocument.Load(filepath);
    
                XElement root = xDocument.Element("Expenses");
                IEnumerable<XElement> rows = root.Descendants("Expense");
                XElement firstRow = rows.First();
    
                firstRow.AddBeforeSelf(new XElement("Expense",
                       new XElement("Id", exp.Id.ToString()),
                       new XElement("Amount", exp.Amount.ToString()),
                       new XElement("Contact", exp.Contact),
                       new XElement("Description", exp.Description),
                       new XElement("Datetime", exp.Datetime)));
                xDocument.Save(filepath);
            }
        }
    }