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