Problem using MobileFormatter.

Problem using MobileFormatter.

Old forum URL: forums.lhotka.net/forums/t/8069.aspx


markell posted on Tuesday, December 01, 2009

Dear sirs and ladies.

I am in the process of porting our CSLA based business logic infrastructure to Silverlight (the client side, of course). In the process I have to transition several types to support IMobileObject interface.
If I understand it correctly, the MobileFormatter infrastructure in general is not limited in any way to the Silverlight platform, thus I unit test my changes on the .NET platform.

And here I have a certain problem. It is probably because I misunderstand something important about the mobile objects. Anyway, I came up with a small code to demonstrate the problem. Observe:

  [Serializable]
  internal class ComplexObject<T> : MobileObject, IEquatable<ComplexObject<T>>
    where T : IEquatable<T>
  {
    private T m_state;

    internal T State
    {
      get { return m_state; }
      set { m_state = value; }
    }

    protected override void OnGetState(SerializationInfo info, StateMode mode)
    {
      info.AddValue("m_state", m_state);
      base.OnGetState(info, mode);
    }

    protected override void OnSetState(SerializationInfo info, StateMode mode)
    {
      base.OnSetState(info, mode);
      m_state = info.GetValue<T>("m_state");
    }

    public bool Equals(ComplexObject<T> other)
    {
      return m_state.Equals(other.m_state);
    }
  }

  public static class Program
  {
    private static object GetDeepCopy(object obj)
    {
      var formatter = new MobileFormatter();
      using (var ms = new MemoryStream())
      {
        formatter.Serialize(ms, obj);
        ms.Position = 0;
        return formatter.Deserialize(ms);
      }
    }

    private static void Test<T>(T value) where T : IEquatable<T>
    {
      var obj = new ComplexObject<T>();
      obj.State = value;
      var copy = (ComplexObject<T>)GetDeepCopy(obj);
      Debug.Assert(obj.Equals(copy));
    }

    public static void Main()
    {
      Test<int>(100);
      Test<string>("100");
      Test<DateTime>(DateTime.Now);
      Test<ComplexObject<int>>(new ComplexObject<int>() { State = 100 });
    }
  }

The code simply creates deep copies of various ComplexObject<T> instances using MobileFormatter and checks that the deep copy actually equals to the original.

The last line fails (see the exception at the end of this post). I guess I have implemented ComplexObject<T>.OnGetState and ComplexObject<T>.OnSetState wrong. What is the correct way to implement them?

Thanks.

The last test fails with the following exception:

System.Runtime.Serialization.SerializationException was unhandled
  Message="Type 'A.ComplexObject`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' with data contract name 'ComplexObjectOfint:http://schemas.datacontract.org/2004/07/A' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer."
  Source="System.Runtime.Serialization"
  StackTrace:
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at WriteSerializationInfo.FieldDataToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
       at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at WriteKeyValueOfstringSerializationInfo.FieldDataOzoZvLrmToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
       at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
       at WriteArrayOfKeyValueOfstringSerializationInfo.FieldDataOzoZvLrmToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
       at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at WriteSerializationInfoToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
       at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
       at WriteArrayOfSerializationInfoToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
       at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
       at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)
       at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
       at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
       at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)
       at Csla.Serialization.Mobile.MobileFormatter.Serialize(XmlWriter writer, Object graph) in c:\Dev\3rd_party\csla\Serialization\Mobile\MobileFormatter.cs:line 86
       at Csla.Serialization.Mobile.MobileFormatter.Serialize(Stream serializationStream, Object graph) in c:\Dev\3rd_party\csla\Serialization\Mobile\MobileFormatter.cs:line 42
       at A.Program.GetDeepCopy(Object obj) in C:\Home\work\A\Program.cs:line 46
       at A.Program.Test[T](T value) in C:\Home\work\A\Program.cs:line 56
       at A.Program.Main() in C:\Home\work\A\Program.cs:line 65
  InnerException:

RockfordLhotka replied on Tuesday, December 01, 2009

I'm not sure MobileObject supports generic types. This isn't a definite answer, just a guess, but I think it is a good guess :)

markell replied on Wednesday, December 02, 2009

Thanks Rocky for the prompt reply.
Here is a sample code without any generics that produces the same regretfully negative result:

  [Serializable]
  internal class SimpleObject : MobileObject, IEquatable<SimpleObject>
  {
    private int m_state;

    internal int State
    {
      get { return m_state; }
      set { m_state = value; }
    }

    protected override void OnGetState(SerializationInfo info, StateMode mode)
    {
      info.AddValue("m_state", m_state);
      base.OnGetState(info, mode);
    }

    protected override void OnSetState(SerializationInfo info, StateMode mode)
    {
      base.OnSetState(info, mode);
      m_state = info.GetValue<int>("m_state");
    }

    public bool Equals(SimpleObject other)
    {
      return m_state.Equals(other.m_state);
    }
  }

  [Serializable]
  internal class ComplexObject : MobileObject, IEquatable<ComplexObject>
  {
    private SimpleObject m_state;

    internal SimpleObject State
    {
      get { return m_state; }
      set { m_state = value; }
    }

    protected override void OnGetState(SerializationInfo info, StateMode mode)
    {
      info.AddValue("m_state", m_state);
      base.OnGetState(info, mode);
    }

    protected override void OnSetState(SerializationInfo info, StateMode mode)
    {
      base.OnSetState(info, mode);
      m_state = info.GetValue<SimpleObject>("m_state");
    }

    public bool Equals(ComplexObject other)
    {
      return m_state.Equals(other.m_state);
    }
  }

  public static class Program
  {
    private static object GetDeepCopy(object obj)
    {
      var formatter = new MobileFormatter();
      using (var ms = new MemoryStream())
      {
        formatter.Serialize(ms, obj);
        ms.Position = 0;
        return formatter.Deserialize(ms);
      }
    }

    private static void TestSimpleObject()
    {
      var obj = new SimpleObject() { State = 100 };
      var copy = (SimpleObject)GetDeepCopy(obj);
      Debug.Assert(obj.Equals(copy));
    }

    private static void TestComplexObject()
    {
      var obj = new ComplexObject() { State = new SimpleObject() { State = 200 } };
      var copy = (ComplexObject)GetDeepCopy(obj);
      Debug.Assert(obj.Equals(copy));
    }

    public static void Main()
    {
      TestSimpleObject();
      TestComplexObject();
    }
  }

TestComplexObject() crashes with the same exception. I must be doing something wrong inside the ComplexObject.OnGetState method.

Regarding your reply. Then why MobileList<T> is supported? It is a generic type after all.

Thank you very much.

markell replied on Wednesday, December 02, 2009

I have found how to make the sample code work. One has to replace the OnGetState and OnSetState overrides in the ComplexObject with the following overrides:

    protected override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
    {
      var childInfo = formatter.SerializeObject(m_state);
      info.AddValue("m_state", childInfo.ReferenceId);
      base.OnGetChildren(info, formatter);
    }

    protected override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
    {
      base.OnSetChildren(info, formatter);
      var referenceId = info.GetValue<int>("m_state");
      m_state = (SimpleObject)formatter.GetObject(referenceId);
    }

Is it the right way to achieve the serialization of nested mobile objects?

Thanks.

RockfordLhotka replied on Wednesday, December 02, 2009

I don't have time to look into this in the near future, as I'm traveling this week and next.

I suggest you look at the unit tests for serialization, as I know we have tests specifically for private backing field serialization. Maybe they will help you identify what is missing/wrong in your test.

markell replied on Wednesday, December 02, 2009

Great, thanks. Have a good trip.

Copyright (c) Marimer LLC