1

We have a website application which acts as a form designer.

The form data is stored in XML file. Each form has it's own xml file.

When i edit a form, i basically recreate the XML file.

public void Save(Guid form_Id, IEnumerable<FormData> formData)
{
    XDocument doc = new XDocument();
    XElement formsDataElement = new XElement("FormsData");
    doc.Add(formsDataElement);
    foreach (FormData data in formData)
    {
        formsDataElement.Add(new XElement("FormData",
                                 new XAttribute("Id", data.Id)
                                 new XAttribute("Name", data.Name)
                                 // other attributes
                             ));
    }

    doc.Save(formXMLFilePath);
}

This works good, but i want to make sure that two users won't update at the same time the XML file. I want to lock it somehow.

How can i individually lock the save process for each file?

I could lock the Save function like below, but this will lock all the users, even if they save a different XML file.

private static readonly object _lock = new object();
public void Save(Guid form_Id, IEnumerable<FormData> formData)
{
    lock(_lock)
    {
        XDocument doc = new XDocument();
        foreach (FormData data in formData)
        {
            // Code
        }

        doc.Save(formXMLFilePath);
    }
}
Catalin
  • 11,503
  • 19
  • 74
  • 147

3 Answers3

2

To prevent files being written simultaneously, the XDocument.Save method internally creates a FileStream with the following constructor:

new FileStream(
    outputFileName,
    FileMode.Create,
    FileAccess.Write,
    FileShare.Read,
    0x1000,
    UseAsync)

The option FileShare.Read prevents multiple writes occuring simultaneously. If you attempted to perform two writes simultaneously, you'd wind up with an IOException being thrown.

You might want to write some code to deal with this eventuality. This answer https://stackoverflow.com/a/50800/138578 should provide exactly how to handle a file being locked and re-trying until the file becomes available.

If your documents are small and many, you shouldn't run into this situation very often, if at all.

Community
  • 1
  • 1
Paul Turner
  • 38,949
  • 15
  • 102
  • 166
1

Try something like this:

FileStream file = new FileStream(formXMLFilePath,FileMode.Create,FileAccress.Write,FileShare.None);

try {
    doc.Save(file);
} finally {
    file.Close();
}

This uses a FileStream and we supply a FileShare mode. In our case we are exclusively locking the file so that only we can write to it.

However, this simply means that subsequent writes will fail as there is a lock.

To get around this problem I tend to have a queue and a separate thread who's only job is to process the queue and write to the file system.

However another solution is to simply try and access the file, and if that fails wait a little bit then try again. This would let you do something akin to lock where it waits until it can access and then proceeds:

private static bool IsLocked(string fileName)
{
    if (!File.Exists(fileName)) return false;

    try {
        FileStream file = new FileStream(fileName,FileMode.Open,FileAccess.Write,FileShare.None);

        try {
            return false;
        } finally {
            file.Close();
        }
    } catch (IOException) {
        return true;
    }
}

public void Save(Guid form_Id, IEnumerable<FormData> formData)
{
    while (IsLocked(formXMLFilePath)) Thread.Sleep(100);

    FileStream file = new FileStream(formXMLFilePath,FileMode.Create,FileAccress.Write,FileShare.None);

    try {
        doc.Save(file);
    } finally {
        file.Close();
    }
}
Lloyd
  • 29,197
  • 4
  • 84
  • 98
  • Looks exactly like what i need! This will block the file even if i open it without using FileStream, right? (Ex: XDocument doc = new XDocument(formXMLFilePath) ). If a user tries to open it and is blocked, the thread will wait until the file is released or will throw an error? – Catalin Jan 14 '13 at 10:40
  • It won't wait. It will just fail. You can specify FileShare.Read to allow readonly access. You will need to marshal access to the file otherwise you will hit the lock and get an exception. This isn't a lock in the sense of a thread lock. – Lloyd Jan 14 '13 at 10:41
  • Can you show me a simple example of how can i create a separate Thread, please? – Catalin Jan 14 '13 at 10:41
  • I've added an example of waiting on a locked file instead. – Lloyd Jan 14 '13 at 10:50
0

You did not closed your xml file connection after opening your xml file. So I think you need to first closed your connection at the end of your function.

you can use this

filename.Close();

I hope this will help you

Ghost Answer
  • 1,458
  • 1
  • 17
  • 41