tags:

views:

141

answers:

6

We recently started working with XML files, after many years of experience with the old INI files.

My coworker found some CodeProject sample code that uses System.Xml.XmlDocument.Save. We are getting exceptions when two programs try to write to the same file at the same time.

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

This seems obvious in hindsight, but we had not anticipated it because accessing INI files via the Win32 API does not have this limitation. I assume there's some arbitration done by the Win32 calls that work at a higher level than the the XmlDocument.Save method.

I'm hoping there are higher level XML routines somewhere in the .Net library that work similarily to the Win32 functions, but don't know where to start looking.

Or maybe we can set up our file access permissions to allow multiple programs to write to the same file?

Time is short (like almost all SW projects), and if we can't find a solution quickly, we'll have to hold our noses and go back to INI files.

+5  A: 

This isn't going to work. Even if the 2 processes could write to the same file, it would just corrupt your file. If you really need multiple programs writing at the same time, I would use a database instead.

Or could you split your single XML file up into separate files? Let each process write to its own file, then combine all of the files together during your "Read" method.

David
+1  A: 

As far as I'm aware, it's difficult to get multiple processes write to a file - how to handle concurrency?

However, if you want one file to read, while the other writes, that will work (as long as you don't mind the reader possibly reading a slightly old file). If so, use the XmlDocument.Load functions passing in a FileStream that was initialized with read only file access.

Many (most?) of the System.Xml namespace will have thread safe overloads too - but it sounds like you want cross process file access so that will not help unless you can collapse all of your instances of your applications into a single process.

So therefore, if I were you I would consider a richer data store that supports multi-process access. A database of some kind would be the most obvious answer.

EDIT: Corrected as per comment.

Reddog
No, see http://msdn.microsoft.com/en-us/library/aa363858(v=VS.85).aspx - you have control whether other processes can simultaneously open the file for read or write using the `FILE_SHARE_READ`, `FILE_SHARE_WRITE` and `FILE_SHARE_DELETE` flags.
Pete Kirkham
Right you are sir... I stand corrected... Will edit answer.
Reddog
@Pete - thanks for the link. I did a little digging, and believe the FileShare enum - http://msdn.microsoft.com/en-us/library/system.io.fileshare(VS.90).aspx - serves the same purpose in .Net.
Tom Bushell
+1  A: 

An alternative to splitting the file would be to use a database - there are XML Databases out there, Wikipedia has some of them listed.

I can't vouch for any of them though.

Christina Mayers
+1  A: 

Well, you can solve your first problem by making sure you close the file when you write it, and retry to open it. You then need to hold it open, read it, merge any changes, then write it out again. (you have to merge the changes, otherwise you will overwrite changes made between your application reading the file and writing it out again).

INI files have been superseded twice - once by the registry, and then by ConfigurationManager You might want to experiment with ConfigurationManager. It appears Configuration.Save will throw an exception if the file has changed or it can't write to it - in which case catch the exception and retry a few times.

Pete Kirkham
A: 

An XML file is much more sensitive to its structure (schema/DTD, etc.) than an INI file. Also, the INI methods in Win32 hide the complexities of file access from you. In theory you could come up with some kind of thread-safe writer that locked at a tag level rather than file level, but it's not an out-of-the-box feature.

GalacticCowboy
Would the anonymous downvoter care to comment? (Pretty sure this was a retribution downvote for a negative comment I made on someone else's answer, even though I did *not* downvote them...)
GalacticCowboy
+2  A: 

If these programs are running in the same session, you can create a separate class to handle writing to files, and use either Singleton (or Multiton to map to many file names) design pattern to guarantee only one instance of this class. This will eliminate your I/O exceptions. Just make sure that you flush the buffer in this new class on each "write to file" request.

Beware: this will introduce a global state to your program. Singleton is considered by some to be an anti-pattern for this reason.

In addition, this may not work well for XML files because they form a tree structure, which means that the whole file has to be re-read before you make a write change to it (you can't simply "append" to the end of the file or you will corrupt it).

I think you need to examine the reason why you are trying to use XML. If you are trying to use it like a "dumb database", you should consider moving to a real "lite" database like SQLite. If you just need to repeatedly append to the end of the file, there is nothing wrong with using a flat file like an .ini.

AbeVoelker