- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Exception when serializing an entity for WCF
Joined: 17-Aug-2005
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)
Joined: 21-Aug-2005
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);
Joined: 17-Aug-2005
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.
Joined: 21-Aug-2005
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.
Joined: 17-Aug-2005
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?!
Joined: 21-Aug-2005
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
Joined: 08-Jan-2008
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?
Joined: 08-Jan-2008
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 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, List
1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary2 processedObjectIDs, List
1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase21.Xml2EntityCollection(XmlReader reader, Dictionary
2 processedObjectIDs, List1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2
1.SD.LLBLGen.Pro. ORMSupportClasses.IEntityCollectionAccess2.Xml2EntityCollection(XmlReader reader, Dictionary2 processedObjectIDs, List
1 nodeEntityReferences)\r\n at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary2 processedObjectIDs, List
1 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 ..."
Joined: 21-Aug-2005
Please use the correct attribute as follows:
**[CustomXmlSerializationAttribute]**
public IDictionary<string,string> BrokenBusinessRules
{
get { return _brokenBusinessRules; }
set { _brokenBusinessRules = value; }
}
Joined: 08-Jan-2008
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.
Joined: 21-Aug-2005
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");
Joined: 08-Jan-2008
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");
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?
Joined: 08-Jan-2008
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?
Joined: 21-Aug-2005
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.
Joined: 08-Jan-2008
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.
Joined: 17-Aug-2003
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.