Bug 11294 - Mono ignores TypeForwardedFrom when serializing
Summary: Mono ignores TypeForwardedFrom when serializing
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: Interop (show other bugs)
Version: unspecified
Hardware: PC Linux
: --- normal
Target Milestone: ---
Assignee: Marek Safar
URL:
Depends on:
Blocks:
 
Reported: 2013-03-20 13:03 UTC by Neale Ferguson
Modified: 2013-07-30 09:51 UTC (History)
4 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Neale Ferguson 2013-03-20 13:03:48 UTC
ObservableCollection was moved from Windows.Base to System and TypeForwardedTo and TypeForwardedFrom tags were added to the relevant source code. ObservableCollection is serializable but when done on a Windows system using .NET >= 3 the serialized object will still contain: 

WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35 

but Mono will use System. Therefore, if you serialize on Mono and then try and deserialize on .NET then you get an error message:

Error when trying to deserialize with ObservableCollection: Unable to find assembly 'System'.

Mono should use the forwarded information to ensure compatibility with older programs (and with what .NET does).

In addition, Windows serializes a variable name _busyCount whereas Mono defines _count.
 
Test case (which also reveals a discrepancy between .NET's and Mono's implementation of BindingList due to mismatching variable names):

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace SerializationPrototype
{
   class Program
   {
      static void Main(string[] args)
      {
         const string observableCollectionFile = "SerializationPrototypeObservableCollection.bin";
         const string bindingListFile = "SerializationPrototypeBindingList.bin";

         if (args.Count() != 1)
         {
            OutputUsageMessage();
            return;
         }

         switch(args[0])
         {
            case "-so":
               // Serializing ObservableCollection
               try
               {
                  Serialize(GetDefaultDataObjectWithObservableCollection(), observableCollectionFile);
               }
               catch (Exception exception)
               {
                  Console.Out.WriteLine("Error when trying to serialize with ObservableCollection: " + exception.Message);
                  return;
               }
               Console.Out.WriteLine("Finished serialization with ObservableCollection.");
               break;
            case "-do":
               // Deserializing ObservableCollection
               DataObjectWithObservableCollection dataObjectWithObservableCollection;
               try
               {
                  dataObjectWithObservableCollection = Deserialize<DataObjectWithObservableCollection>(observableCollectionFile);
               }
               catch (Exception exception)
               {
                  Console.Out.WriteLine("Error when trying to deserialize with ObservableCollection: " + exception.Message);
                  return;
               }
               if (IsDataObjectDefault(dataObjectWithObservableCollection))
               {
                  Console.Out.WriteLine("Deserialization with ObservableCollection successful.");
               }
               else
               {
                  Console.Out.WriteLine("Deserialization with ObservableCollection failed.");
               }
               break;
            case "-sb":
               // Serializing BindingList
               try
               {
                  Serialize(GetDefaultDataObjectWithBindingList(), bindingListFile);
               }
               catch (Exception exception)
               {
                  Console.Out.WriteLine("Error when trying to serialize with BindingList: " + exception.Message);
                  return;
               }
               Console.Out.WriteLine("Finished serialization with BindingList.");
               break;
            case "-db":
               // Deserializing BindingList
               DataObjectWithBindingList dataObjectWithBindingList;
               try
               {
                  dataObjectWithBindingList = Deserialize<DataObjectWithBindingList>(bindingListFile);
               }
               catch (Exception exception)
               {
                  Console.Out.WriteLine("Error when trying to deserialize with BindingList: " + exception.Message);
                  return;
               }
               if (IsDataObjectDefault(dataObjectWithBindingList))
               {
                  Console.Out.WriteLine("Deserialization with BindingList successful.");
               }
               else
               {
                  Console.Out.WriteLine("Deserialization with BindingList failed.");
               }
               break;
            default:
               OutputUsageMessage();
               break;
         }
      }


      #region Methods for objects with ObservableCollection

      private static DataObjectWithObservableCollection GetDefaultDataObjectWithObservableCollection()
      {
         ObservableCollection<string> collection = new ObservableCollection<string> { "bar", "car", "var" };
         return new DataObjectWithObservableCollection { StringValue = "foo", DoubleValue = 123.0, Collection = collection };
      }

      private static bool IsDataObjectDefault(DataObjectWithObservableCollection dataObject)
      {
         if (dataObject == null || dataObject.Collection == null)
         {
            return false;
         }

         DataObjectWithObservableCollection defaultDataObject = GetDefaultDataObjectWithObservableCollection();

         if (dataObject.Collection.Count != defaultDataObject.Collection.Count)
         {
            return false;
         }

         bool isDefault = true;
         isDefault &= (dataObject.StringValue == defaultDataObject.StringValue);
         isDefault &= (dataObject.DoubleValue == defaultDataObject.DoubleValue);
         for (int i = 0; i < dataObject.Collection.Count; i++)
         {
            isDefault &= (dataObject.Collection[i] == defaultDataObject.Collection[i]);
         }

         return isDefault;
      }

      #endregion


      #region Methods for objects with BindingList

      private static DataObjectWithBindingList GetDefaultDataObjectWithBindingList()
      {
         BindingList<string> collection = new BindingList<string> { "bar", "car", "var" };
         return new DataObjectWithBindingList { StringValue = "foo", DoubleValue = 123.0, Collection = collection };
      }

      private static bool IsDataObjectDefault(DataObjectWithBindingList dataObject)
      {
         if (dataObject == null || dataObject.Collection == null)
         {
            return false;
         }

         DataObjectWithBindingList defaultDataObject = GetDefaultDataObjectWithBindingList();

         if (dataObject.Collection.Count != defaultDataObject.Collection.Count)
         {
            return false;
         }

         bool isDefault = true;
         isDefault &= (dataObject.StringValue == defaultDataObject.StringValue);
         isDefault &= (dataObject.DoubleValue == defaultDataObject.DoubleValue);
         for (int i = 0; i < dataObject.Collection.Count; i++)
         {
            isDefault &= (dataObject.Collection[i] == defaultDataObject.Collection[i]);
         }

         return isDefault;
      }

      #endregion


      #region Output

      private static void OutputUsageMessage()
      {
         Console.Out.WriteLine("Usage:");
         Console.Out.WriteLine("Serialize ObservableCollection: SerializationPrototype -so");
         Console.Out.WriteLine("Deserialize ObservableCollection: SerializationPrototype -do");
         Console.Out.WriteLine("Serialize BindingList: SerializationPrototype -sb");
         Console.Out.WriteLine("Deserialize BindingList: SerializationPrototype -db");
      }

      #endregion


      #region Serialization Support

      private static void Serialize(object dataObject, string outputFile)
      {
         IFormatter formatter = new BinaryFormatter();
         Stream stream = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None);
         formatter.Serialize(stream, dataObject);
         stream.Close();
      }

      private static T Deserialize<T>(string sourceFile)
      {
         Stream stream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.None);
         IFormatter formatter = new BinaryFormatter();
         T dataObject = (T) formatter.Deserialize(stream);
         stream.Close();

         return dataObject;
      }

      #endregion

   }


   #region Support Classes

   [Serializable]
   public class DataObjectWithObservableCollection
   {
      private string m_stringValue;
      private double m_doubleValue;
      private ObservableCollection<string> m_collection;

      public string StringValue
      {
         get { return m_stringValue; }
         set { m_stringValue = value; }
      }

      public double DoubleValue
      {
         get { return m_doubleValue; }
         set { m_doubleValue = value; }
      }

      public ObservableCollection<string> Collection
      {
         get { return m_collection; }
         set { m_collection = value; }
      }
   }

   [Serializable]
   public class DataObjectWithBindingList
   {
      private string m_stringValue;
      private double m_doubleValue;
      private BindingList<string> m_collection;

      public string StringValue
      {
         get { return m_stringValue; }
         set { m_stringValue = value; }
      }

      public double DoubleValue
      {
         get { return m_doubleValue; }
         set { m_doubleValue = value; }
      }

      public BindingList<string> Collection
      {
         get { return m_collection; }
         set { m_collection = value; }
      }
   }

   #endregion

}
Comment 1 Miguel de Icaza [MSFT] 2013-04-24 12:07:49 UTC
Neale, I saw a few commits go in, and later some reverted patches.

What is the end result?
Comment 2 Marek Safar 2013-07-30 09:51:47 UTC
Fixed in master