Le réseau pour les développeurs > Forums - Accueil > Visual Basic General > Writing to XML with file locking - what am I missing?
Poser une questionPoser une question
 

TraitéeWriting to XML with file locking - what am I missing?

  • lundi 2 novembre 2009 18:32FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     A du code

    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

  • mercredi 4 novembre 2009 14:56Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     Traitée
    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."

Toutes les réponses

  • lundi 2 novembre 2009 18:40Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    try adding this line after you save it.

    xmlError.Save( ..... )
    holdFile.Flush()



    Mark the best replies as answers. "Fooling computers since 1971."
  • lundi 2 novembre 2009 20:18FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • lundi 2 novembre 2009 20:47jwavila Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    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.

  • lundi 2 novembre 2009 20:47Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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."
  • lundi 2 novembre 2009 22:47FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • lundi 2 novembre 2009 22:53FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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

  • mardi 3 novembre 2009 04:18Cor LigthertMVPMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 17:15FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 17:27Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    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."
  • mardi 3 novembre 2009 18:00FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 18:08Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    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."
  • mardi 3 novembre 2009 18:17Cor LigthertMVPMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 18:24FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 18:54Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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."
  • mardi 3 novembre 2009 20:07FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     A du code
    Rudy,

    Ok, let me try again.

    Given the following code:
    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/>
    
    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:

    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

     

  • mardi 3 novembre 2009 20:16FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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


  • mardi 3 novembre 2009 20:20FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mardi 3 novembre 2009 20:37Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    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."
  • mercredi 4 novembre 2009 14:56Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     Traitée
    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."
  • mercredi 4 novembre 2009 19:03FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    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

  • mercredi 4 novembre 2009 19:23Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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."
  • mercredi 4 novembre 2009 20:10FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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
  • mercredi 4 novembre 2009 20:19Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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."
  • mercredi 4 novembre 2009 20:21FletcherJ Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     A du code
    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:

    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
    
    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?

    Thanks,

    Fletcher



  • mercredi 4 novembre 2009 20:37Rudedog2 Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    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."