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)

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


Attachments

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

Note You need to log in before you can comment on or make changes to this bug.