WP7 IsolatedStorage XML file empty after app update

Go To StackoverFlow.com

0

I submitted an update for my WP7 app and I've had users complain that their data was erased after the update. I am storing my data by serializing my ViewModel class as an XML file in IsolatedStorage. During my testing I noticed that updating the app caused the settings file to get partially overwritten with a serialized copy of a new instance (with default values) of my ViewModel object. I thought I had solved the problem by using FileMode.Create when I write to my XML file, but I guess not.

Could the serialization have gone wrong because I added new properties to the ViewModel object and deserializing from the existing XML file failed? I do have my code set to instantiate a new ViewModel object if one cannot be read from the XML file. If this is the case, does it mean I cannot add any new properties to my ViewModel object?

Edit:
Here is the structure of my ViewModel, not really all that complex:

public class MyClass: INotifyPropertyChanged
{
    public MyClass()
    {
        // Set defaults
        this.Items= new ObservableCollection<Item>();
        this.TextTemplate = "default";
        this.HasSeenSomething = false;
    }

    public ObservableCollection<Item> Items { get; set; }
    // New properties added in app update
    public string TextTemplate { get; set; }
    public bool HasSeenSomething { get; set; }
}

Here is the code I'm using to serialize/deserialize my ViewModel. I think it is pretty standard but maybe I've botched something:

public static void WriteToXml<T>(T data, string path)
{
    var xmlWriterSettings = new XmlWriterSettings { Indent = true };
    using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (var stream = myIsolatedStorage.OpenFile(path, FileMode.Create))
        {
            var serializer = new XmlSerializer(typeof(T));
            using (var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
            {
                serializer.Serialize(xmlWriter, data);
            }
        }
    }
}

public static T ReadFromXml<T>(string path)
{
    T data = default(T);
    try
    {
        using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (myIsolatedStorage.FileExists(path))
            {
                using (var stream = myIsolatedStorage.OpenFile(path, FileMode.Open))
                {
                    try
                    {
                        var serializer = new XmlSerializer(typeof(T));
                        object instance = serializer.Deserialize(stream);
                        if (instance != null) data = (T)instance;
                    }
                    catch (System.Exception ex)
                    {
                        var e = ex;
                    }
                }
            }
        }
    }
    catch(System.Exception ex) 
    {
        var e = ex;
    }
    return data;
}
2012-04-04 20:23
by Brent Keller


0

You can, but there are a few gotchas.

  1. You need to make sure you are not changing properties in a way that will break when they deserialize. So changing Int to String is okay (though I wouldn't suggest), but the opposite could break.
  2. You need to remember that the ctor does not get called when you deserialize your object. That means that if you do, in the ctor, the following:

    public MyClass() { MyV2Collection = new Collection(); }

And you have no other mechanism that will new up the collection if it's null, you will die on NullRef the first time you try to access the property.

Dime to dollars, I'd bet you are hitting pt #2.

Fixing it is fairly easy, but you need to remember to do it. The options are:

  1. In the property accessor, check if the backing field is null and instantiate it if it is.
  2. Create a function (public) that has the [OnDeserialized] attribute decorating it. That code will run when deserialization is done and you can instantiate your member there.
2012-04-04 22:38
by Shahar Prish
I don't think that a null collection is the problem. The collection would have data in it or else the user wouldn't have had anything to be lost. The new properties I added were a string and bool, nothing that should have prevented the model from deserializing - Brent Keller 2012-04-05 11:49
Gotcha. The best thing to do then is just test it - install your old binary on the emulator, run it etc, use an Isolated Storage tool to grab the file, install the new binary, place the old file inside the isolated storage and debug it. http://wptools.codeplex.com - Shahar Prish 2012-04-05 16:18
After deploying (via install) my v1.0 xap to my device and saving some items, I deployed v1.1 (via update) and the data was intact (with the new properties set to their defaults). I tried this several times with different data each time and I was not able to had the data corrupted. The only way I did manage to get the corruption was when I update from v1.1 to v1.0. Which totally makes sense because the properties wouldn't align. I have no idea what could have happened for the customer that complained - Brent Keller 2012-04-06 02:35
How big can the data be? My apps employ a "backup & save" mechanism to make sure I don't accidently lose data on a crash-during-save or app-forcible-shutdown during save. You may want to do something similar - Shahar Prish 2012-04-06 03:56
Even if they have a pretty large collection of items, the size of the data won't be all that big since it's mostly just integers. I think adding a backup copy before saving is a good idea and I'll implement that in my app. Thanks for the advice on that - Brent Keller 2012-04-06 13:08
Ads