Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Versioning the hard way

Versioning the hard way

Scheduled Pinned Locked Moved C#
questiongraphicsdesignbeta-testingxml
4 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    L Offline
    lukeer
    wrote on last edited by
    #1

    Hello experts, even though I tried to create a minimal code sample, this question needs rather large code samples attached. Don't worry, all the interesting text is here, above all the code. An application has to serialize data to a file and to de-serialize from that file. This works like a charm. The working example is attached in the first two code samples. Now for the interesting part: the original working version is delivered to millions of satisfied customers. All their positive feedback encouraged the creation of version 2. Version 2 should of course be able to load data files from version 1. It is possible to change the data model without breaking compatibility, to some extend at least using ObsoleteAttribute and OptionalFieldAttribute. But when changes get more fundamental, other approaches are to be researched. The third code sample shows an altered DataContainer class. In this example, it has to serialize one field that is not present in the original version. Therefore the well-known loading process fails. To recover from the exception, another assembly is loaded. In my test case, it's the original assembly. It's delivered within the version 2 project resources. The original assembly should load the legacy file. But it throws a TargetInvocationException instead. Its inner exception tells us that DataContainer has three members while there are only two de-serialized. Now where does this legacy assembly know of DataContainer having three members nowadays? It should have called its integrated version of DataContainer, which then had two members and therefore should load the legacy file without moaning. However, here is the "minimal" working sample:

    // User Interface (unchanged throughout versions)
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    namespace Test_Serialization_Versioning_Heavy
    {
    public partial class Form1 : Form
    {
    #region Declarations

        string \_filename = string.Empty;
        DataContainer \_dataContainer = new DataContainer();
    
        #endregion
    
    
        #region Init
    
        public Form1()
        {
            \_filename = System.IO.Path.Combine(Application.StartupPath, "datafile.xml");
            InitializeComponent();
            ShowData();
        }
    
        #endregion
    
    A B 2 Replies Last reply
    0
    • L lukeer

      Hello experts, even though I tried to create a minimal code sample, this question needs rather large code samples attached. Don't worry, all the interesting text is here, above all the code. An application has to serialize data to a file and to de-serialize from that file. This works like a charm. The working example is attached in the first two code samples. Now for the interesting part: the original working version is delivered to millions of satisfied customers. All their positive feedback encouraged the creation of version 2. Version 2 should of course be able to load data files from version 1. It is possible to change the data model without breaking compatibility, to some extend at least using ObsoleteAttribute and OptionalFieldAttribute. But when changes get more fundamental, other approaches are to be researched. The third code sample shows an altered DataContainer class. In this example, it has to serialize one field that is not present in the original version. Therefore the well-known loading process fails. To recover from the exception, another assembly is loaded. In my test case, it's the original assembly. It's delivered within the version 2 project resources. The original assembly should load the legacy file. But it throws a TargetInvocationException instead. Its inner exception tells us that DataContainer has three members while there are only two de-serialized. Now where does this legacy assembly know of DataContainer having three members nowadays? It should have called its integrated version of DataContainer, which then had two members and therefore should load the legacy file without moaning. However, here is the "minimal" working sample:

      // User Interface (unchanged throughout versions)
      using System;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Data;
      using System.Drawing;
      using System.Text;
      using System.Windows.Forms;

      namespace Test_Serialization_Versioning_Heavy
      {
      public partial class Form1 : Form
      {
      #region Declarations

          string \_filename = string.Empty;
          DataContainer \_dataContainer = new DataContainer();
      
          #endregion
      
      
          #region Init
      
          public Form1()
          {
              \_filename = System.IO.Path.Combine(Application.StartupPath, "datafile.xml");
              InitializeComponent();
              ShowData();
          }
      
          #endregion
      
      A Offline
      A Offline
      Andre Kraak
      wrote on last edited by
      #2

      You will have to make use of version tolerance: Version Tolerant Serialization[^].

      0100000101101110011001000111001011101001

      L 1 Reply Last reply
      0
      • A Andre Kraak

        You will have to make use of version tolerance: Version Tolerant Serialization[^].

        0100000101101110011001000111001011101001

        L Offline
        L Offline
        lukeer
        wrote on last edited by
        #3

        Generally, that would be the good answer. As I already mentioned "...at least using ObsoleteAttribute and OptionalFieldAttribute..." I am aware of the possibilities of Version Tolerant Serialization. But in this case, I need something more powerful. Hence my approach to load a legacy assembly and make it load the legacy file. Problem is, the legacy assembly throws an error on loading a file that it should be capable of loading. The error in turn refers to a data type that the legacy assembly cannot know of. How can I make this work?

        Ciao, luker

        1 Reply Last reply
        0
        • L lukeer

          Hello experts, even though I tried to create a minimal code sample, this question needs rather large code samples attached. Don't worry, all the interesting text is here, above all the code. An application has to serialize data to a file and to de-serialize from that file. This works like a charm. The working example is attached in the first two code samples. Now for the interesting part: the original working version is delivered to millions of satisfied customers. All their positive feedback encouraged the creation of version 2. Version 2 should of course be able to load data files from version 1. It is possible to change the data model without breaking compatibility, to some extend at least using ObsoleteAttribute and OptionalFieldAttribute. But when changes get more fundamental, other approaches are to be researched. The third code sample shows an altered DataContainer class. In this example, it has to serialize one field that is not present in the original version. Therefore the well-known loading process fails. To recover from the exception, another assembly is loaded. In my test case, it's the original assembly. It's delivered within the version 2 project resources. The original assembly should load the legacy file. But it throws a TargetInvocationException instead. Its inner exception tells us that DataContainer has three members while there are only two de-serialized. Now where does this legacy assembly know of DataContainer having three members nowadays? It should have called its integrated version of DataContainer, which then had two members and therefore should load the legacy file without moaning. However, here is the "minimal" working sample:

          // User Interface (unchanged throughout versions)
          using System;
          using System.Collections.Generic;
          using System.ComponentModel;
          using System.Data;
          using System.Drawing;
          using System.Text;
          using System.Windows.Forms;

          namespace Test_Serialization_Versioning_Heavy
          {
          public partial class Form1 : Form
          {
          #region Declarations

              string \_filename = string.Empty;
              DataContainer \_dataContainer = new DataContainer();
          
              #endregion
          
          
              #region Init
          
              public Form1()
              {
                  \_filename = System.IO.Path.Combine(Application.StartupPath, "datafile.xml");
                  InitializeComponent();
                  ShowData();
              }
          
              #endregion
          
          B Offline
          B Offline
          BobJanova
          wrote on last edited by
          #4

          You may need to load the assembly into a separate AppDomain – but that then makes it impossible to copy the object across once it's loaded in the other domain, so it doesn't really help. With my lobby client (which loads its game type modules at runtime and does some version control on them) I had big problems trying to get two version of the same class to operate within one AppDomain. If the changes are more than just adding new fields, you might want to think about having the class unchanged and creating a new class which inherits from or wraps it. You might also want to look at the design: typically you should be serialising data, i.e. very simple data-containing objects, and I wouldn't expect them to change so much as to make the version tolerance in the framework insufficient. (The kind of thing you're experiencing here is one reason why I don't like to use [Serializable] for long term persistence, i.e. saving to file or other permanent storage. I typically have read/write methods on the persistent classes which convert them to the string or bytes that are needed to recreate each object – often not the same thing as its complete internal state, which is what serialisation saves – and those methods can handle the reading of prior versions, particularly if you pass as a parameter the version which the file claims to be.)

          1 Reply Last reply
          0
          Reply
          • Reply as topic
          Log in to reply
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes


          • Login

          • Don't have an account? Register

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • World
          • Users
          • Groups