Problem deserializing derived entities with WCF

Posts   
 
    
ww
User
Posts: 83
Joined: 01-Oct-2004
# Posted on: 19-Jul-2011 23:21:15   

LLBLGen v. 3.1. Runtime version 3.1.11.415.

I am using the two-class Adapter configuration, sending entities over WCF. So for entity "Test" I get base class TestEntity and derived class MyTestEntity.

I have an operation defined as:


EntityCollection<MyTestEntity> GetTestEntities();

On the server, I create an EntityCollection<MyTestEntity>, populate it using DataAccessAdapter.FetchEntityCollection, and return it.

On the client I get this exception:


System.ArgumentException : Object of type 'TestEntity' cannot be converted to type 'MyTestEntity'.
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.Xml2EntityCollection(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\AdapterSpecific\EntityCollectionBase2.cs:line 1049
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.ReadXml(XmlReader reader, XmlFormatAspect format) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\AdapterSpecific\EntityCollectionBase2.cs:line 720
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\AdapterSpecific\EntityCollectionBase2.cs:line 1855
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.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.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.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.DeserializeReply(Message message, Object[] parameters)
at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)


When I switched the return type to List<MyTestEntity> instead, it got a little further, but then threw the same sort of exception when trying to deserialize one of the other entities referenced by MyTestEntity.

Any ideas?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Jul-2011 07:00:25   

Do you have any custom elements in your entity (like custom properties)? If so maybe you should use Custom Member Serialization. This is a related thread about that exception: http://llblgen.com/tinyforum/Messages.aspx?ThreadID=11078

How your DataContract looks like?

David Elizondo | LLBLGen Support Team
ww
User
Posts: 83
Joined: 01-Oct-2004
# Posted on: 20-Jul-2011 16:07:16   

There are no custom elements in MyTestEntity.

If I change my operation to use the base class (EntityCollection<TestEntity>) instead, it works correctly.

I am not using a data contract; I have all of the entity classes added as known types for the service.

I just did a test where I serialized to a file using XmlSerializer. When I serialized the collection, it shows the correct factory to use but marks the entity with the base class:


<EntityCollectionOfMyTestEntity>
- <EntityCollection Factory="DataClasses.MyTestEntityFactory, DataClasses" Format="Compact25">
- <TestEntity EntityType="28" ObjectID="65b06a6f-8661-4e55-99f1-9564d77ec0bb">

When I serialized just a since instance of MyTestEntity, it also was written as "TestEntity" in the XML.

One possible explanation has occurred to me: my entity classes aren't actually named TestEntity and MyTestEntity--I've just been writing it that way in this discussion to keep things simple. But I have modified the templates to generate the classes as "TestEntityBase" for the base class and "TestEntity" for the derived class.

Possibly I have the wrong string in the code somewhere that's causing the framework to generate the wrong name in the XML? Where is the serialization code getting the entity name that it uses?

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 20-Jul-2011 17:44:38   

It's hard to know what's going on at your side without a repro solution. So could you please send us a simple repro solution. As small and minimum as possible.

Thanks.

ww
User
Posts: 83
Joined: 01-Oct-2004
# Posted on: 20-Jul-2011 22:35:08   

I did a simple repro solution using my customized templates, and deserialization works correctly for the two-class model. Deserialization (from a file) does not work for my actual project using the same templates and settings--same error as through WCF. So I need to work through the code and figure out what's going on that's keeping it from deserializing properly. Doesn't seem to be a problem with the framework, but if anyone has seen similar issues and has suggestions on what to look at...

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 21-Jul-2011 10:00:09   

Could you please try following the example in the docs, and return IEntityCollection2 from the service method.

ww
User
Posts: 83
Joined: 01-Oct-2004
# Posted on: 21-Jul-2011 18:02:40   

I have cut WCF out of my testing to keep things simple, and am able to reproduce the problem using XML serialization to/from a file, using very simple entities and the standard templates (C#/.NET 4.0/Adapter.TwoClasses2010).

The code is failing when I deserialize a derived class that is in an inheritance hierarchy. I have attached a ZIP containing my LLBLGen project, generated code, and demo app that creates, serializes, and deserializes entities.

First I have an entity called Thing which produces ThingEntity and MyThingEntity. I can serialize/deserialize EntityCollection<MyThingEntity> just fine ("things.xml" in the ZIP).

Then I have an inheritance hierarchy: entity Test, with SpecializedTest inheriting from it, producing SpecializedTestEntity and MySpecializedTestEntity. If I serialize/deserialize EntityCollection<MySpecializedTestEntity> ("specializedentities.xml" in the ZIP) it fails on deserialization:


System.InvalidOperationException: There is an error in XML document (4, 6). ---> System.InvalidCastException: Unable to cast object of type 'llbltest.EntityClasses.SpecializedTestEntity' to type 'llbltest.EntityClasses.MySpecializedTestEntity'.
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.Xml2EntityCollection(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences) in c:\\Myprojects\\VS.NET Projects\\LLBLGen Pro v3.1\\Frameworks\\LLBLGen Pro\\RuntimeLibraries\\ORMSupportClasses\\AdapterSpecific\\EntityCollectionBase2.cs:line 1051
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.ReadXml(XmlReader reader, XmlFormatAspect format) in c:\\Myprojects\\VS.NET Projects\\LLBLGen Pro v3.1\\Frameworks\\LLBLGen Pro\\RuntimeLibraries\\ORMSupportClasses\\AdapterSpecific\\EntityCollectionBase2.cs:line 720
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader) in c:\\Myprojects\\VS.NET P
rojects\\LLBLGen Pro v3.1\\Frameworks\\LLBLGen Pro\\RuntimeLibraries\\ORMSupportClasses\\AdapterSpecific\\EntityCollectionBase2.cs:line 1855
   at System.Xml.Serialization.XmlSerializationReader.ReadSerializable(IXmlSerializable serializable, Boolean wrappedAny)
   at System.Xml.Serialization.XmlSerializationReader.ReadSerializable(IXmlSerializable serializable)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderEntityCollection1.Read1_Item()
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
   at testapp.Program.DeserializeEntities() in E:\\appdev\\llbltest\\testapp\\Program.cs:line 87

I tried this with Test marked as abstract and non-abstract, and it fails either way. If Test is not abstract and I serialize/deserialize a collection of MyTestEntity, that fails as well.

This isn't included in the demo program but if I serialize/deserialize EntityCollection<SpecializedEntity> (instead of EntityCollection<MySpecializedEntity>) it works correctly.

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 21-Jul-2011 19:14:05   

Thanks for the repro solution - they don't get much better than that as it demonstrated the issue perfectly...!

We'll investigate the issue and get back to you.

Matt

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Jul-2011 12:27:22   

Bug in template (entity factory classes for derived types). They don't override the method CreateEntityFromEntityTypeValue(), which calls into the general general entity factory, and this goes wrong as it uses the normal entity factory.

The my*factory classes lack an override.

Looking into the fix.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 22-Jul-2011 12:38:07   

Copy the attached file into: <llblgen pro installation folder>\Frameworks\LLBLGen Pro\Templates\SharedTemplates\Net2.x\C#

as administrator. Then re-generate your code.

Attachments
Filename File size Added on Approval
derivedEntityFactoriesAdapter.template 6,888 22-Jul-2011 12:38.14 Approved
Frans Bouma | Lead developer LLBLGen Pro
ww
User
Posts: 83
Joined: 01-Oct-2004
# Posted on: 22-Jul-2011 19:56:51   

That fixed it. Thanks for the quick turnaround.