locked
Releasing a StorageFile After Writing

    Question

  • I have 2 extension methods that I am using to save and load my data:
    Public Module SaveLoadExtensions
    	<Extension()> Public Async Sub Save(players As PlayerData())
    		System.Diagnostics.Debug.WriteLine("Save")
    		Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("Players.xml", CreationCollisionOption.ReplaceExisting)
    		Dim xml As New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players"))
    		Dim sw As New StringWriter()
    		xml.Serialize(sw, players)
    		Await FileIO.WriteTextAsync(tempfile, sw.ToString())
    	End Sub
    	<Extension()> Public Async Function Load(players As PlayerData()) As Task(Of PlayerData())
    		System.Diagnostics.Debug.WriteLine("Load")
    		Try
    			Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.GetFileAsync("Players.xml")
    			Dim sr As New StringReader(Await FileIO.ReadTextAsync(tempfile))
    			Return CType(New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players")).Deserialize(sr), PlayerData())
    		Catch ex As FileNotFoundException
    			'File does not exist
    			Return {}
    		End Try
    	End Function
    End Module
    When I call the Save() method the first time, it works great, but the 2nd time (and supposedly every time after that) it give an UnauthorizedAccessException. I am pretty sure this is because my method has not yet released the file handle, but I am not sure how to do this. If I set tempfile=Nothing, I get an error, so that would not work. I could not find any methods to dispose of the StorageFile, so what can I do to prevent the UnauthorizedAccessException? Thanks.

    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Thursday, August 14, 2014 7:24 PM

Answers

  • I had tried both of those, but neither one worked. However, I think I have found the solution. It involves doing the Tasks manually, but I think that is necessary in order to insure that they finish before I try to do something else:
    Public Module SaveLoadExtensions
    	<Extension()> Public Sub Save(players As PlayerData())
    		System.Diagnostics.Debug.WriteLine("Save - Start")
    		'Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("Players.xml", CreationCollisionOption.ReplaceExisting)
    		'Dim xml As New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players"))
    		'Dim sw As New StringWriter()
    		'xml.Serialize(sw, players)
    		'Await FileIO.WriteTextAsync(tempfile, sw.ToString())
    		Dim tempfiletask As Task(Of StorageFile) = ApplicationData.Current.LocalFolder.CreateFileAsync("Players.xml", CreationCollisionOption.ReplaceExisting).AsTask()
    		Dim xml As New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players"))
    		Dim sw As New StringWriter()
    		xml.Serialize(sw, players)
    		tempfiletask.Wait()
    		Dim writetexttask As Task = FileIO.WriteTextAsync(tempfiletask.Result, sw.ToString()).AsTask()
    		writetexttask.Wait()
    		System.Diagnostics.Debug.WriteLine("Save - End")
    	End Sub
    	<Extension()> Public Async Function Load(players As PlayerData()) As Task(Of PlayerData())
    		System.Diagnostics.Debug.WriteLine("Load")
    		Try
    			'Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.GetFileAsync("Players.xml")
    			'Dim sr As New StringReader(Await FileIO.ReadTextAsync(tempfile))
    			'Return CType(New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players")).Deserialize(sr), PlayerData())
    			Dim tempfiletask As Task(Of StorageFile) = ApplicationData.Current.LocalFolder.GetFileAsync("Players.xml").AsTask()
    			tempfiletask.Wait()
    			Dim sr As New StringReader(Await FileIO.ReadTextAsync(tempfiletask.Result))
    			System.Diagnostics.Debug.WriteLine("Load - Successful")
    			Return CType(New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players")).Deserialize(sr), PlayerData())
    		Catch aggregateex As AggregateException
    			System.Diagnostics.Debug.WriteLine("Load - AggregateException")
    			If TypeOf (aggregateex.InnerException) Is FileNotFoundException Then : Return {}
    			Else : Return {}
    			End If
    		End Try
    	End Function
    End Module
    Notice that I create a Task and then call the Wait() method. My previous code is commented out so you can more easily see how I changed it. The only question I have (although I don't know if it would make a huge difference) is how to start a task but not require it to be finished until later. What I mean by this is, in my Save() method, I wait a couple statements after creating the Task (tempfiletask) before calling the Wait() method, therefore the Task is not being worked on until after those other statements are done executing. If I call the Start() method, it does not let me call Wait(), and if I call Wait() immediately after creating the Task, then the Task finishes before starting the other statements, therefore still not running at the same time. How can I make the Task (tempfiletask) run at the same time as the other statements? Isn't that the whole point of asynchronization? Thanks.

    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Friday, August 15, 2014 5:26 PM

All replies

  • I think that because the method is Async and sometimes gets called more than once, CreateFileAsync is getting called the second time before the first one has finished. However, because CreateFileAsync is asynchronous, I am not sure how to avoid this. What is the best way to make sure the first call is finished before the next one tries to access the file? Thanks.

    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Friday, August 15, 2014 2:50 AM
  • Hi Nathan,

    Have you tried with Using Statement? And you can also assign an null to your tempfile to release the current file.

    --James


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Friday, August 15, 2014 9:40 AM
    Moderator
  • I had tried both of those, but neither one worked. However, I think I have found the solution. It involves doing the Tasks manually, but I think that is necessary in order to insure that they finish before I try to do something else:
    Public Module SaveLoadExtensions
    	<Extension()> Public Sub Save(players As PlayerData())
    		System.Diagnostics.Debug.WriteLine("Save - Start")
    		'Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("Players.xml", CreationCollisionOption.ReplaceExisting)
    		'Dim xml As New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players"))
    		'Dim sw As New StringWriter()
    		'xml.Serialize(sw, players)
    		'Await FileIO.WriteTextAsync(tempfile, sw.ToString())
    		Dim tempfiletask As Task(Of StorageFile) = ApplicationData.Current.LocalFolder.CreateFileAsync("Players.xml", CreationCollisionOption.ReplaceExisting).AsTask()
    		Dim xml As New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players"))
    		Dim sw As New StringWriter()
    		xml.Serialize(sw, players)
    		tempfiletask.Wait()
    		Dim writetexttask As Task = FileIO.WriteTextAsync(tempfiletask.Result, sw.ToString()).AsTask()
    		writetexttask.Wait()
    		System.Diagnostics.Debug.WriteLine("Save - End")
    	End Sub
    	<Extension()> Public Async Function Load(players As PlayerData()) As Task(Of PlayerData())
    		System.Diagnostics.Debug.WriteLine("Load")
    		Try
    			'Dim tempfile As StorageFile = Await ApplicationData.Current.LocalFolder.GetFileAsync("Players.xml")
    			'Dim sr As New StringReader(Await FileIO.ReadTextAsync(tempfile))
    			'Return CType(New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players")).Deserialize(sr), PlayerData())
    			Dim tempfiletask As Task(Of StorageFile) = ApplicationData.Current.LocalFolder.GetFileAsync("Players.xml").AsTask()
    			tempfiletask.Wait()
    			Dim sr As New StringReader(Await FileIO.ReadTextAsync(tempfiletask.Result))
    			System.Diagnostics.Debug.WriteLine("Load - Successful")
    			Return CType(New XmlSerializer(GetType(PlayerData()), New XmlRootAttribute("Players")).Deserialize(sr), PlayerData())
    		Catch aggregateex As AggregateException
    			System.Diagnostics.Debug.WriteLine("Load - AggregateException")
    			If TypeOf (aggregateex.InnerException) Is FileNotFoundException Then : Return {}
    			Else : Return {}
    			End If
    		End Try
    	End Function
    End Module
    Notice that I create a Task and then call the Wait() method. My previous code is commented out so you can more easily see how I changed it. The only question I have (although I don't know if it would make a huge difference) is how to start a task but not require it to be finished until later. What I mean by this is, in my Save() method, I wait a couple statements after creating the Task (tempfiletask) before calling the Wait() method, therefore the Task is not being worked on until after those other statements are done executing. If I call the Start() method, it does not let me call Wait(), and if I call Wait() immediately after creating the Task, then the Task finishes before starting the other statements, therefore still not running at the same time. How can I make the Task (tempfiletask) run at the same time as the other statements? Isn't that the whole point of asynchronization? Thanks.

    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Friday, August 15, 2014 5:26 PM