Exception when serializing an entity for WCF

Posts   
 
    
Barry
User
Posts: 232
Joined: 17-Aug-2005
# Posted on: 03-Oct-2007 12:09:37   

I'm using v2.5.7.924 runtime, I got the following exception when I pass an entity to a WCF service, and but it does not happen to every entity, just some with complex structure. And it does not have any problems before I migrate to v2.5.

How can I debug the entity and find out what's wrong with it?


Object reference not set to an instance of an object.
   at SD.LLBLGen.Pro.ORMSupportClasses.XmlHelper.XmlValueToObject(String typeName, String xmlValue)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.Xml2EntityCollection(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.SD.LLBLGen.Pro.ORMSupportClasses. IEntityCollectionAccess2.Xml2EntityCollection(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.ReadXml(XmlReader reader, XmlFormatAspect format)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, Boolean isMemberType)
   at System.Runtime.Serialization.XmlDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex. InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns)
   at System.Runtime.Serialization.NetDataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)
   at System.Runtime.Serialization.NetDataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameter(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameters(XmlDictionaryReader reader, PartInfo[] parts, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 03-Oct-2007 16:10:43   

Would you please perform a simple serialize/deserialize test like the following one?

    ProductTcEntity product = new ProductTcEntity(25);
    using(DataAccessAdapter adapter = new DataAccessAdapter())
    {
        adapter.FetchEntity(product);
    }

    string xml;
    product.WriteXml(out xml);
    ProductTcEntity deserialized = new ProductTcEntity();
    deserialized.ReadXml(xml);
    string deserializedXml;
    deserialized.WriteXml(out deserializedXml);
    Assert.AreEqual(xml, deserializedXml);
Barry
User
Posts: 232
Joined: 17-Aug-2005
# Posted on: 04-Oct-2007 04:13:07   

Exception throw when running the line deserialized.ReadXml(xml);

I trace the coding of XmlHelper.cs, exception occur at the line 405, the typeName is "Unknown" and it return a null, therefore it cause exception in the following line.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 04-Oct-2007 11:37:35   

Do you have custom complex properties in your entity? If so, then please read the following lines from the docs:

Custom Member serialization/deserialization If you add your own member variables to entity classes including their properties, you probably also want these values to be serialized and deserialized into the XML stream. Normally, a custom member exposed as a read/write property is serialized as a string using the ToString() method of the value of the custom property. In a lot of cases this isn't sufficient and you want to perform your own custom xml serialization/deserialization on the value of this custom property, for example if this custom property represents a complex object. To signal that the LLBLGen Pro runtime framework has to call custom xml serialization code for a given property, the property has to be annotated with a CustomXmlSerializationAttribute attribute. When a property is seen with that attribute, LLBLGen Pro will call the entity method entity.PerformCustomXmlSerialization to produce valid XML for the custom property. Likewise, when deserializing an XML stream into entities, the LLBLGen Pro runtime framework will call, when it runs into a property annotated with a CustomXmlSerializationAttribute, the method entity.PerformCustomXmlDeserialization to deserialize the xml for the property into a valid object. You should override these methods in a partial class of the entity which contains the custom properties.

Custom property serialization/deserialization is a feature of the Compact25 xml format, which is used by Adapter in Webservices/WCF scenarios.

Barry
User
Posts: 232
Joined: 17-Aug-2005
# Posted on: 04-Oct-2007 11:45:27   

Yes, it has a custom property, but it is read only and only has a getter. I need to add XmlIgnore attribute to those read only properties? Since LLBLGen v2.0 runtime does not have this problem in WCF, it's so strange in v2.5.

And, ReadXml() would try to deserialize value for a property without a setter?!

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 04-Oct-2007 15:55:45   

Use XmlIgnore, to prevent the value from being deserialized on the other side.

If you want to have the value on the other side, you should create the routine which serializes/deserializes the value as described in the piece of doc I've quoted. That routine could then set the inner member variable on deserialization for example

al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 09-Jan-2008 02:38:11   

Walaa wrote:

Do you have custom complex properties in your entity? If so, then please read the following lines from the docs:

Custom Member serialization/deserialization If you add your own member variables to entity classes including their properties, you probably also want these values to be serialized and deserialized into the XML stream. Normally, a custom member exposed as a read/write property is serialized as a string using the ToString() method of the value of the custom property. In a lot of cases this isn't sufficient and you want to perform your own custom xml serialization/deserialization on the value of this custom property, for example if this custom property represents a complex object. To signal that the LLBLGen Pro runtime framework has to call custom xml serialization code for a given property, the property has to be annotated with a CustomXmlSerializationAttribute attribute. When a property is seen with that attribute, LLBLGen Pro will call the entity method entity.PerformCustomXmlSerialization to produce valid XML for the custom property. Likewise, when deserializing an XML stream into entities, the LLBLGen Pro runtime framework will call, when it runs into a property annotated with a CustomXmlSerializationAttribute, the method entity.PerformCustomXmlDeserialization to deserialize the xml for the property into a valid object. You should override these methods in a partial class of the entity which contains the custom properties.

Custom property serialization/deserialization is a feature of the Compact25 xml format, which is used by Adapter in Webservices/WCF scenarios.

Hi, Walaa

I use LLBL v 2.5 (release date Dec 16th 2007) and WCF. I've tried to follow the way you proposed but still experiencing problems with serialization/deserialization.

My case is: I have a custom property in a CommonEntityBase class which should be serializable. So whenever I pass child entity to client side via WCF the property and its value should be available. The property is of IDictionary type. Because of that I've created a custom serializer for IDictionary. I've marked the property with CustomXmlSerializationAttribute and overrided PerformCustomXmlSerialization and PerformCustomXmlDeserialization methods in CommonEntityBase class.


[DataContract]
        public abstract partial class CommonEntityBase : EntityBase2
        {

        IDictionary<string,string> _brokenBusinessRules = new Dictionary<string,string>();

        [SD.LLBLGen.Pro.ORMSupportClasses.CustomXmlSerialization]
        public IDictionary<string,string> BrokenBusinessRules
        {
            get { return _brokenBusinessRules; }
            set { _brokenBusinessRules = value; }
        }       
    
        protected override void PerformCustomXmlSerialization(PropertyDescriptor descriptor, object propertyValue, XmlWriter writer, XmlFormatAspect aspects)
        {
            if (descriptor.Name == "BrokenBusinessRules")
            {
                DictionarySerializer serializer = new DictionarySerializer(BrokenBusinessRules);
                serializer.WriteXml(writer);
            }
        }

        protected override void PerformCustomXmlDeserialization(PropertyDescriptor descriptor, XmlReader reader)
        {
            if (descriptor.Name == "BrokenBusinessRules")
            {
                DictionarySerializer dictionarySerializer = new DictionarySerializer(BrokenBusinessRules);
                dictionarySerializer.ReadXml(reader);
            }
        }   
     }



public class DictionarySerializer : IXmlSerializable
    {
        const string NS = "http://company.com/xml/serialization";

        public IDictionary<string, string> dictionary;

        public DictionarySerializer(IDictionary<string, string> dictionary)
        {
            this.dictionary = dictionary;
        }

        public void WriteXml(XmlWriter w)
        {
            w.WriteStartElement("dictionary", NS);
            foreach (string key in dictionary.Keys)
            {
                string value = dictionary[key];
                w.WriteStartElement("item", NS);
                w.WriteElementString("key", NS, key);
                w.WriteElementString("value", NS, value);
                w.WriteEndElement();
            }
            w.WriteEndElement();
        }

        public void ReadXml(XmlReader r)
        {
            r.Read();
            r.ReadStartElement("dictionary", NS);
            while (r.NodeType != XmlNodeType.EndElement)
            {
                r.ReadStartElement("item", NS);
                string key = r.ReadElementString("key", NS);
                string value = r.ReadElementString("value", NS);
                r.ReadEndElement();
                r.MoveToContent();
                dictionary.Add(key, value);
            }
             r.Read();
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }
     }

The serialization doesn't work properly in this scenario. May be I'm doing smth wrong? Could you help me with this question and point out a right solution?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 09-Jan-2008 11:30:04   

The serialization doesn't work properly in this scenario.

Would you please elaborate on this? We can't know how exactly it doesn't work simple_smile Is there an exception? (if so please post it with the stack trace).

al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 10-Jan-2008 01:05:46   

Walaa wrote:

The serialization doesn't work properly in this scenario.

Would you please elaborate on this? We can't know how exactly it doesn't work simple_smile Is there an exception? (if so please post it with the stack trace).

Hi Walaa,

Thank you for your reply.

Actually I've got an exception while trying to send an entity to a client via WCF

"The Xml deserialization routine EntityBase2.Xml2Entity encountered a problem: the reader encountered an unexpected EndElement, with name 'BrokenBusinessRules'."

The stack trace is:

"\r\nServer stack trace: \r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase21.Xml2EntityCollection(XmlReader reader, Dictionary2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase21.SD.LLBLGen.Pro. ORMSupportClasses.IEntityCollectionAccess2.Xml2EntityCollection(XmlReader reader, Dictionary2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.ReadXml(XmlReader reader, XmlFormatAspect format)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, Boolean isMemberType)\r\n at System.Runtime.Serialization.XmlDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)\r\n at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameter(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)\r\n at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)\r\n at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)\r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)\r\n at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)\r\n at contract.IUserService.Save(UserEntity user)\r\n at ServiceProxies.UserClient.Save(UserEntity user) in ..."

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 10-Jan-2008 11:40:01   

Please use the correct attribute as follows:

    **[CustomXmlSerializationAttribute]**
    public IDictionary<string,string> BrokenBusinessRules
    {
        get { return _brokenBusinessRules; }
        set { _brokenBusinessRules = value; }
    }       
al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 10-Jan-2008 12:51:08   

Walaa wrote:

Please use the correct attribute as follows:

    **[CustomXmlSerializationAttribute]**
    public IDictionary<string,string> BrokenBusinessRules
    {
        get { return _brokenBusinessRules; }
        set { _brokenBusinessRules = value; }
    }       

Hi,

This is the same thing.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 10-Jan-2008 15:11:59   

I'm not sure about your Dictionary serliazation/Deserialization routine, but I found the following link: http://aspzone.com/blogs/john/articles/167.aspx

Also I think in the ReadXml you should use the following r.ReadStartElement("dictionary", NS); instead of: r.ReadStartElement("dictionary");

al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 10-Jan-2008 15:58:39   

Walaa wrote:

I'm not sure about your Dictionary serliazation/Deserialization routine, but I found the following link: http://aspzone.com/blogs/john/articles/167.aspx

Also I think in the ReadXml you should use the following r.ReadStartElement("dictionary", NS); instead of: r.ReadStartElement("dictionary");

simple_smile Thanks, I've already read this article and I based on it while playing with this serialization dilemma.

It seems to me I know what the problem might be in.

Probably LLBL uses XmlFormatAspect.Compact for serialization/deserialization.

I'm sending LLBL generated entity via WCF. How can I enforce LLBL to use XmlFormatAspect.Compact25 for serialization/deserialization? Is there any attribute going along with CustomXmlSerializationAttribute to enforce XmlFormatAspect.Compact25 usage?

al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 11-Jan-2008 06:55:42   

I've created a unit test


        [Test]
        public void ResidentialAddressTest()
        {
            AddressEntity address = GetCorrectAddress();
            AddressBO addressBO = new ResidentialAddressBO(address);
            Assert.IsTrue(addressBO.IsValid);

            address.AddStreet = String.Empty;
            Assert.IsFalse(addressBO.IsValid);

            string xml = String.Empty;
            address.WriteXml(XmlFormatAspect.Compact25, out xml);

            StreamWriter writer = new StreamWriter("Address.xml");
            writer.Write(xml);
            writer.Close();
            
            AddressEntity clone = new AddressEntity();
            clone.ReadXml(xml);
            Assert.AreEqual(address.BrokenBusinessRules.Count, clone.BrokenBusinessRules.Count);
            Assert.IsTrue(clone.BrokenBusinessRules.Count > 0);

            address.WriteXml(out xml); //not Compact25
            AddressEntity newClone = new AddressEntity();
            newClone.ReadXml(xml);
            Assert.IsTrue(newClone1BrokenBusinessRules.Count  != address.BrokenBusinessRules.Count);
        }

So, as I understand serialization works fine if LLBL uses Compact25. And fails if it uses Compact.

As far as I send the whole LLBL generated entity, How can I enforce LLBL to use Compact25?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 11-Jan-2008 12:35:34   

CustomXmlSerializationAttribute is Compact25 specific. (i.e. not used except in Compact25 serialization).

For EntityFields, Compact25 just emits the value of the field rather than other properties as in the case of verboseXML.

I think the issue lies in your serialization/deserialization routine of the dictionary property.

al_au
User
Posts: 6
Joined: 08-Jan-2008
# Posted on: 13-Jan-2008 06:43:51   

Walaa wrote:

CustomXmlSerializationAttribute is Compact25 specific. (i.e. not used except in Compact25 serialization).

For EntityFields, Compact25 just emits the value of the field rather than other properties as in the case of verboseXML.

I think the issue lies in your serialization/deserialization routine of the dictionary property.

The thing is that the unit test passes successfully. As I think, serialization and deserialization of IDictionary have been implemented properly, because if I use address.WriteXml(XmlFormatAspect.Compact25, out xml) then both serialization and deserialization are successful. But if I use address.WriteXml(out xml) then both serialization and deserialization are with mistakes.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 13-Jan-2008 13:47:04   

Correct, WriteXml() doesn't default to Compact25, as it is a method which was there previously (introduced in one of the earliest versions) so we couldn't make it suddenly produce other XML, as that would break a lot of applications. Therefore you should use the overload which accepts the format specification.

Frans Bouma | Lead developer LLBLGen Pro