Writing to XML with file locking - what am I missing?
Hi all,
I have some code based on some previous posts that just won't work right. I am sure it's something simple, but I just can't find it....
The code all runs without error. Here are the key lines:' strErrorLog holds a valid file name with full path ' xmlNewError holds a valid xElement value Using holdFile As New IO.FileStream(strErrorLog, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite, IO.FileShare.None) Dim xmlError As System.Xml.Linq.XElement = System.Xml.Linq.XElement.Load(System.Xml.XmlReader.Create(holdFile)) ' Now insert this as the first element in the log. xmlError.AddFirst(xmlNewError) Try xmlError.Save(System.Xml.XmlWriter.Create(holdFile)) Catch ex As Exception Debug.Assert(False, "Ok, now what do we do?") End Try End Using
When I step through the code, everything works. There is no error on the xmlError.Save call. However, the actual file is NOT updated. If I remove the "Using" commands and change the save to "xmlError.Save(strErrorLog)", then it writes just fine.... but I lose the locking...
It's got to be something really simple, but it's frustrating that I don't get an error message... But it does unlock the file.... It just doesn't update it.....
Any help is greatly appreciated!
Thanks,
FletcherJ
Réponses
- I think this will work as I think you are expecting.
Private Sub TestMethod2()
Dim xmlError As XElement
Using file As New FileStream(strErrorLog, FileMode.OpenOrCreate, FileAccess.ReadWrite)
Using reader As New XmlTextReader(file)
xmlError = XElement.Load(reader)
reader.Close()
End Using
End Using
Using file As New FileStream(strErrorLog, FileMode.Create, FileAccess.ReadWrite)
Using writer As New XmlTextWriter(file, System.Text.Encoding.UTF8)
'Dim xmlError As XElement = XElement.Load(XmlReader.Create(file))
xmlError.AddFirst(xmlNewError)
Try
xmlError.Save(writer)
writer.Flush()
writer.Close()
Catch ex As Exception
Debug.Assert(False, "Ok, now what do we do?")
End Try
End Using
End Using
End Sub
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971."- Marqué comme réponseMartin Xie - MSFTMSFT, Modérateurmardi 10 novembre 2009 03:12
Toutes les réponses
- try adding this line after you save it.
xmlError.Save( ..... )
holdFile.Flush()
Mark the best replies as answers. "Fooling computers since 1971." - Rudedog2,
I tried that, but it doesn't seem to have any effect. There is still no physical update to the xml file nor is there any error indicating why it might not have committed the data to disk.
Any other thoughts?
Thanks,
FletcherJ I think I remember something about the FileAccess.ReadWrite with FileStream
Something in the back of my brain is pointing to that, but I just can't remember the specifics :(
Try moving the Try above the Using line and move the End Try below the End USing
If that doesn't work, maybe try reading the file in using XMLDocument (you can search the Forums for examples); it's something like:
Dim doc as XMLDocument
doc.Load(filename)
and then work on it. Sorry these are just a couple of guesses, I usually only work with XML as a mini-database.- I dunno.
Normally, one would call Flush on the actual object doing the actual writing to disk, not the Stream object. But that is not possible in your case because you do not exactly declare an XmlWriter object.
Dim writer as XmlWriter = new XmlTextWriter(holdFile, Encoding.UTF8)
xmlError.Save(writer)
writer.Flush()
Something like that.
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971." - jwavila,
I tried moving the Try/Catch outside of the using, no effect. I haven't tried using XMLDocument yet because everything I have is based on using XElement... And I am not quite sure of the effects of changing to XMLDocument....
If nothing else works, I will try that.
Thanks,
FletcherJ - Rudy,
Could it be that I have the wrong syntax in the save call? It strikes me as odd that I would be using:
xmlError.Save(System.Xml.XmlWriter.Create(holdFile))
as that indicates to me that I am creating something based on holdfile and then writing it (vs the contents of xmlError) out to disk. This is just code I received from someone else who thought it would work.
I tried to use other options, but holdfile is based on IO.FileStream and the save methods all want textwriter or xmlwriter. Do you think I could try casting the call to a textwriter or similar to get it to work?
Or could it be that the whole statement is backwards? I would think that since holdfile is a filestream of sorts, I would be passing the xmlError object to it to write out. Or at least the reference to holdfile should not be based on a .Create method....
But I am really new to this and not quite sure. It seems that there are many ways to do things and they don't seem to be very intuitive or consistent. But that may just be me....
Anyway, any ideas are appreciated.
Thanks,
FletcherJ Rudy,
Could it be that I have the wrong syntax in the save call? It strikes me as odd that I would be using:
Yea, the best way would be to close the readed file, like Rudy wrote and then give it using IO.File.Move(oldpath,newpath) another location to save your new one on th old path, and then to delete the replaced file IO.File.Delete(newpath).
That prevents you that somehthing goes wrong which you don't want for instance at unexpected power down.
Success
Cor- Cor,
I don't know if I understand your response. Are you saying to write the data to a temp file, then delete the original and rename the temp file to the new name? This won't really work in a multi user environment as it is quite possible that a new file will be created after the original is deleted and the new temp file has been renamed. Other reasons aside, I would prefer to put a file lock on the XML file and just write to it, then remove the lock.
The Using approach seems to work (the file is clearly locked), it just doesn't appear to write out the data, nor does it generate an error indicating why it doesn't write it out.
My guess is that it is simply a syntax issue. I just need to find out what I need to tweek to get it to work right.....
The thing that surprises me is that this is such a simple thing, I don't understand why it appears to be so difficult....
Thanks,
Fletcher
You need to have the courage to just simply toss out what you have and re-write it from scratch if need be.
Here's an example of calling that save method. Perhaps it will shed some light on Cor's remarks.
XmlElement.Save(XmlWriter)
Note the use of the keyword Using to Dispose of the Writer object.
Mark the best replies as answers. "Fooling computers since 1971."- Rudy,
I have already tossed out a couple of approaches. I have no problem doing that. What is frustrating is that it never occured to me that getting a file lock on an XML file would be so difficult..... I kinda thought that, given the whole push on XML by MS, that this would be a fairly simple operation.
Anyway, I am open to anyway to do it. And am about to start looking at what you suggested.
When you say to toss out what I have, do you mean to just from using XElement to using XMLDocument? I was under the impression that the XElement approach was supposed to replace XMLDocument. I have no problem switching, I just don't want to switch to make things work when MS is going in a different direction. Or am I missing the point?
Thanks,
Fletcher
File lock is not the problem.
You don't seem to be writing anything in a file to disk.
And I suspect for a good reason. Poorly formed Xml.
Since we do not know what your file should look like, I have no answer to your question.
My gut answer is "no". Is the file supposed to be well formed Xml?
You appear to be trying to append Xml Elements onto the end of an 'Xml" file.
Where about in the file is the element being written?
What about the root node that needs to be closed? shouldn't that be last?
Simply appending elements onto a text file is not XML.
Mark the best replies as answers. "Fooling computers since 1971."- Fletcher,
From your previous messages I could not see if you was attempting to use an XML file for a kind of multiuser solution.
But I had doubt if that was your intention.
Multi user with a normal flat file what an XML file still is, is impossible. It misses the possibilities to update like a database only certain pieces of data. Jet has it, but that has a complete binary mechanisme inside.
Success
Cor - Rudy,
Ok, I am lost. The link you mention is the XElement.save method. This works just fine. But the XML file is not locked. I need a lock on the file to get some details, update them, then write it back to the file. During this process, I need to prevent others from doing the same.
I was told to use the "Using" syntax and the code I have above it based on the code I was given. It works great except that it doesn't save.
If I just use the XElement.save method, I get an error because the file is locked by the holdfile filestream object (which is what I would expect.) So I need to figure out what is wrong with the xmlError.Save(...) syntax above so that it writes out the data.
If there is another way to lock the XML file so I can write, please tell me what it is.
Thanks,
Fletcher - Fletcher,
I asked about root elements in your xml file previously.
I am not able to test your code---which means I cannot reproduce your issue---because your posted code does not result in well formed xml.
I get an exception on the xmlError.AddFirst method call because my file lacks a root node.
I am not getting as far as xmlError.Save at all.
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971." - Rudy,
Ok, let me try again.
Given the following code:
It acts like it works, but the data is not saved. If I change it to the way it is currently written, it looks like this:Using holdFile As New IO.FileStream(strErrorLog, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite, IO.FileShare.None) Dim xmlError As System.Xml.Linq.XElement = System.Xml.Linq.XElement.Load(System.Xml.XmlReader.Create(holdFile)) ' Now insert this as the first element in the log. xmlError.AddFirst(xmlNewError) Try xmlError.Save(System.Xml.XmlWriter.Create(holdFile)) Catch ex As Exception Debug.Assert(False, "Ok, now what do we do?") End Try End Using<br/>
xmlError = XElement.Load(strErrorLog) ' Now insert this as the first element in the log. xmlError.AddFirst(xmlNewError) Try xmlError.Save(strErrorLog) Catch ex As Exception Debug.Assert(False, "Ok, now what do we do?") End Try <br/><br/><br/><br/><br/><br/><br/><br/>
In both cases, you can assume that strErrorLog and xmlNewError exist and are valid. The second code block works just fine and is what I am currently using. BUT IT DOESN"T SUPPORT MULTIPLE USERS....
So I posed this question and was told to use the "Using" construct. And was given some code as an example of how to make this work. So I implemented it and everything is fine except that it doesn't write anything on the xmlError.Save(strErrorLog) line AND the line doesn't generate an error.I am REALLY easy. Given that the second code block works, all I am trying to do is to figure out how to get a file lock on the XML file before I write the new values. If there is a better way than using "Using", let me know, please! But an example of some code that works would be really appreciated....
Thanks,
Fletcher
- Rudy,
Given that I have not included any code that has XML, it would make sense that anything you tried would fail.
If you want to implement my second block of code (from the email to Rudy), just create a simple XML file. It can be as simple as:
<?xml version="1.0" encoding="utf-8"?>
<Errors/>
Save that and then set strErrorLog as a string pointing the the file you created.
For xmlNewError, just use:
Dim xmlNewError As New XElement(<ThisError>
<ErrorNo>3</ErrorNo>
<ErrorMsg>Some message</ErrorMsg>
</ThisError>)
Now you can test to your hearts desire.
Sorry I didn't include these as they actual contents of the file or the node being added are insignificant to the issue - which is how to get a lock on an XML file when editing it.
I am also looking at using FileStream.Lock - but this would need to implemented on such a low/granular level that I can't believe this is my only option.
Thanks,
Fletcher - Rudy,
One other point. You say that I appear to be trying to append xml Elements onthe the end of an XML file. But if you look at the code, I am using the following syntax:
xmlError.AddFirst(xmlNewError)
You might note that the method I am using is "AddFirst" - which puts the new element as the FIRST node. So I am confused as to why you believe I am trying to append the data?
Of course, it could be that the USING syntax is the culpret that is leaving you to believe this. In which case, that could be the problem. I don't know as I can't quite figure out the documentation on USING - especially in this way. I am just using the code someone gave me to use.
Which is why I suspect that one of the lines referencing holdFile is the culpret.
Thanks,
Fletcher
Fletcher, you are using AddFirst, not Add. I'm just a bit visually impaired. My execution was not getting that far, I had not noticed.
Mark the best replies as answers. "Fooling computers since 1971."- I think this will work as I think you are expecting.
Private Sub TestMethod2()
Dim xmlError As XElement
Using file As New FileStream(strErrorLog, FileMode.OpenOrCreate, FileAccess.ReadWrite)
Using reader As New XmlTextReader(file)
xmlError = XElement.Load(reader)
reader.Close()
End Using
End Using
Using file As New FileStream(strErrorLog, FileMode.Create, FileAccess.ReadWrite)
Using writer As New XmlTextWriter(file, System.Text.Encoding.UTF8)
'Dim xmlError As XElement = XElement.Load(XmlReader.Create(file))
xmlError.AddFirst(xmlNewError)
Try
xmlError.Save(writer)
writer.Flush()
writer.Close()
Catch ex As Exception
Debug.Assert(False, "Ok, now what do we do?")
End Try
End Using
End Using
End Sub
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971."- Marqué comme réponseMartin Xie - MSFTMSFT, Modérateurmardi 10 novembre 2009 03:12
Rudy,
Great. I did some testing and still have a few problems (sorry...)
1) It appears that the FileMode.Create parameter was erasing the prior contents of the xml file, so I changed that to OpenOrCreate. If this mucks things up in any way, please let me know.2) The file appears to become unlocked after the reader.close statement (even before the first "End Using" statement. This is a problem because I need to get a lock on the file when I read it in. And I have to keep the lock while I addFirst a new element, then save it. With the file lock being released.
I did try to move the "Using reader..." block after the second "Using file ..." statement, but that didn't work (as you probably knew.)
3) I was hoping that maybe I could use a different xmlText... class - which I could use to read and write, but it appears that my only options are xmlTextReader and xmlTextWriter...I am trying some other things to see what I can figure out. But at least this gets me much further ahead than I was before.
Of course, if you have an idea of how to get around losing the lock before I do the write, that would be way cool.
Thanks,
Fletcher
- Fletcher,
1) You tell me if it meets your needs.
Most of the time you would want to append the end of the file, not pre-pend at the start of it.
2) Why not enclose the contents of the method in a SynchLock block.
3) There are other ways to manipulate XML files. My comfort zone is with DataSets, and XmlDataDocuments.
Rudy =8^D
Mark the best replies as answers. "Fooling computers since 1971." - Rudy,
1) Given the way the file is used, the AddFirst is the desired way to add data to the xml file. This appears to work fine.
2) I don't use a SynchLock because no one has suggested it until now. I will look into it.
3) I agree. I don't care which approach I use as long as I can read/write the XML data is a safe (ie with file locking) way.
What I want to do is not complex. I just don't understand why it's so hard to get a file lock on a file, read the contents, then write it back out again....
Any ideas are appreciated.
Fletcher - Why not enclose the contents of the method in a SynchLock block?
Class simpleMessageList
Public messagesList() As String = New String(50) {}
Public messagesLast As Integer = -1
Private messagesLock As New Object
Public Sub addAnotherMessage(ByVal newMessage As String)
SyncLock messagesLock
messagesLast += 1
messagesList(messagesLast) = newMessage
End SyncLock
End Sub
End Class
That bold text can be accessed by only one thread at a time.
Mark the best replies as answers. "Fooling computers since 1971." - Rudy,
Given I want a lock before I read and for it to last until I write, I have tried to modify your code. Below appears to work except for one (significant) problem. When the xmlError.Save(writer) line executes (and the subsequent flush and close), it appears to append the xml data to the end of the xml file instead of replacing the contents. here is the modified code:
If I open the resulting XML file and delete what was their before, then it looks great. Do you know what I might change in the code above to get it to replace the contents rather than append to what was there?Private Sub TestMethod4() Dim xmlError As XElement Dim strErrorLog As String = "TestFile.xml" Dim xmlNewError As New XElement(<TheError ErrorId="111"> <Type>0</Type> <User>Fletcher</User> <Method>MyTestMethod</Method> </TheError>) Using file As New IO.FileStream(strErrorLog, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite) Using reader As New XmlTextReader(file) xmlError = XElement.Load(reader) Using writer As New XmlTextWriter(file, System.Text.Encoding.UTF8) xmlError.AddFirst(xmlNewError) xmlError.Save(writer) writer.Flush() writer.Close() End Using End Using End Using End Sub
Thanks,
Fletcher - My original code this morning replaced the contents with new stuff.
You didn't seem to like it.
Private Shared xmlFileLockObject As New Object
Private Sub TestMethod2()
SyncLock xmlFileLockObject
Dim xmlError As XElement 'this object preserves existing contents
Using file As New FileStream(strErrorLog, FileMode.OpenOrCreate, FileAccess.ReadWrite)
Using reader As New XmlTextReader(file)
xmlError = XElement.Load(reader) ' read existing contents
reader.Close()
End Using
End Using
Using file As New FileStream(strErrorLog, FileMode.Create, FileAccess.ReadWrite)
Using writer As New XmlTextWriter(file, System.Text.Encoding.UTF8)
xmlError.AddFirst(xmlNewError) 'modify existing contents
Try
xmlError.Save(writer) ' overwrite existing file
writer.Flush()
writer.Close()
Catch ex As Exception
Debug.Assert(False, "Ok, now what do we do?")
End Try
End Using
End Using
End SyncLock
End Sub
Mark the best replies as answers. "Fooling computers since 1971."

