views:

34

answers:

2

I'm writing a plug-in for another program through an API. To save it's current state, my plugin stores an instance of a Project object to the file by serializing it to XML and storing it in a user string, a feature provided by the API. It also stores a separate string that contains the version number of the plug-in currently being used.

When a user opens a file my plug-in checks the version numbers. If the current plug-in version is not the same as the version stored in the file it pops up a message warning the user about the different version and that the file might cause the plug-in to crash.

I'd much rather provide a set of migration scripts that run automatically when a user opens an older file in a newer version of the plug-in. My question though is where do these usually go and how are they organized?

Also say my Project class changes significantly between versions where trying to deserialize the project from an old file will fail with the new Project class. I don't want to keep copies of every version of the Project class is my assembly, but at the same time having to parse through the XML would be even more painful. Assuming parsing the XML is the best option can anyone suggest a more organized way of doing this then the code below?

public string MigrateProject(int fileVersion, int plugInversion, string proj)
{
    if(fileVersion>plugInversion)
    {
       //tell user to upgrade their copy of the plugin
       return null;
    }

    if(fileVersion ==1)
    { 
        string v2 = Migrate1to2(serializedProject);
        string v3 = Migrate2to3(v2);
        string v4 = Migrate3to4(v3);
        return v4;
    }
    else if(fileVersion ==2)
    { 
        string v3 = Migrate2to3(serializedProject);
        string v4 = Migrate3to4(v3);
        return v4;
    }
    else if(fileVersion ==3)
    { 
        string v4 = Migrate3to4(serializedProject);
        return v4;
    }
    else 
    {
         //could not migrate project message
         return null;
    }
}
+1  A: 

XmlSerializer is not version tolerant, without you including a version field which you can act on in doing deserialization manually.

BinaryFormatter supports versions as does SoapFormatter and DataContractSerializer.

You would still need to handle the conversion, and could easily cut code this way:

if(fileVersion ==1)
{ 
    serializedProject = Migrate1to2(serializedProject);
    fileVersion = 2;
}
if(fileVersion ==2)
{ 
    serializedProject = Migrate2to3(serializedProject);
    fileVersion = 3;
}
if(fileVersion ==3)
{ 
    serializedProject = Migrate3to4(serializedProject);
    fileVersion = 4
}
else 
{
     //could not migrate project message
     return null;
}
Mikael Svenson
A: 

Store your migrate methods in a list like this:

List<Func<string,string>> myMigrateMethods = new List<Func<string,string>>();
myMigrateMethods.Add(Migrate1To2);
myMigrateMethods.Add(Migrate2To3);
myMigrateMethods.Add(Migrate3To4);

Then iterate through the list starting at the appropriate method:

public string MigrateProject(int fileVersion, int plugInversion, string proj)
{
    if(fileVersion>plugInversion)
    {
       //tell user to upgrade their copy of the plugin
       return null;
    }

    //user already at max version
    if(fileVersion >= (myMigrateMethods.Length-1)) return null;

    var firstMigrateMethodNeeded = (fileVersion-1); //array is 0-based

    var output = serializedProject;
    for(var i= firstMigrateMethodNeeded; i< myMigrateMethods.Length; i++)
    {
       output = myMigrateMethods[i](output);
    }

    return output;

}
charoco