Forum:  Bugs & Issues

Thread:  EntityCollection WCF Deserialization


saggett (User)   Posted on: 15-Nov-2007 16:20:49.
I'm passing an Entity Collection as a parameter in this Service Contract method and I'm encountering an ArgumentNullException, "Value cannot be null.\r\nParameter name: g".

I'm calling my WCF Service via this method:

[OperationContract]
        void SaveEntityCollection(IEntityCollection2 collection, IEntityCollection2 deletedEntities, bool recurse);

In this case, collection is null and deletedEntities is of type EntityCollection and contains three AddOnCostEntity entities. These entities do not possess any child collections or custom properties.

I'm running LLBLGen v2.5 Final (October 25th, 2007) and my runtime library version is 2.5.07.1019.

Here's the stack trace:

at System.Guid..ctor(String g)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(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.EntityCollectionBase2`1.Xml2EntityCollection(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.ReadXml(XmlReader reader, XmlFormatAspect format)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2`1.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.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)
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.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)

and here's the trace log:

Method Enter: EntityCollectionBase2.EntityCollection2Xml
Method Enter: EntityBase2.Entity2Xml
Method Exit: EntityBase2.Entity2Xml
Method Enter: EntityBase2.Entity2Xml
Method Exit: EntityBase2.Entity2Xml
Method Enter: EntityBase2.Entity2Xml
Method Exit: EntityBase2.Entity2Xml
Method Enter: EntityBase2.Entity2Xml
Method Exit: EntityBase2.Entity2Xml
Method Enter: EntityBase2.Entity2Xml
Method Exit: EntityBase2.Entity2Xml
Method Exit: EntityCollectionBase2.EntityCollection2Xml
Method Enter: EntityCollectionBase2.Xml2EntityCollection(XmlReader..)
Method Exit: EntityCollectionBase2.Xml2EntityCollection(XmlReader...
Method Enter: EntityCollectionBase2.Xml2EntityCollection(XmlReader..)
Method Enter: EntityBase2.Xml2Entity(XmlReader...
Method Enter: EntityBase2.Xml2Entity(XmlReader...
Method Exit: EntityBase2.Xml2Entity(XmlReader...
Method Exit: EntityBase2.Xml2Entity(XmlReader...
Method Exit: EntityCollectionBase2.Xml2EntityCollection(XmlReader...

Hope you can help.

Thanks, Stephen

Otis (LLBLGen Pro Team)   Posted on: 15-Nov-2007 17:33:49.
Could you please run with the Debug build of the ormsupport classes dll (is in the runtime libs folder) by also placing the .pdb file in the app folder (on the deserialization side) so we can see the line numbers? Regular Smiley

saggett (User)   Posted on: 16-Nov-2007 11:58:03.
Did that, I'm now stepping through the lines of the ORMSupport code which is very handy indeed. Regular Smiley

Here's the stack trace I now receive with the exception (no idea why this one doesn't give the complete stack, we've just got the very top level):

at System.Guid..ctor(String g)
at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.Xml2Entity(XmlReader reader, Dictionary`2 processedObjectIDs, List`1 nodeEntityReferences) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v2.0\RuntimeLibraries 2.5 .NET 2.x\ORMSupportClasses\EntityBase2.cs:line 1085

I was able to check the fields used in the Xml2Entity method, and found out that startElementName equals 'AddOnCostType' when the error occurs, which is a child entity of AddOnCostEntity via a m:1 relation, and the XmlReader.Value is empty. In this case, AddOnCost.AddOnCostType equals null on each of the three AddOnCost Entities being serialized / deserialized.


Otis (LLBLGen Pro Team)   Posted on: 16-Nov-2007 17:28:55.
Good info. Are the entities in the 'deletedEntities' entities which were tracked by a tracker collection ? If so, they're marked for deletion which will make serialization code chop off the hierarchy frm these entities. This means that related entities aren't serialized with them, as they only are deleted, not the complete graph.

It could be there's a bug in that code somewhere. Could you confirm that the entities in deletedEntities are from a tracker collection (i.e. 'deletedEntities' was the collection assigned to RemovedEntitiesTracker)

If you manually grabbed these entities and stored them in a collection, we've to setup a different test.


saggett (User)   Posted on: 16-Nov-2007 18:29:18.
The entities in deletedEntities are from a tracker collection, and so they're all marked for deletion, but the collection they're passed in isn't the collection.RemovedEntitiesTracker collection. This is the line of code used to make the service call:

endPoint.SaveEntityCollection(CreateNonGenericCollection(collection.DirtyEntities), CreateNonGenericCollection(collection.RemovedEntitiesTracker), recurse);

And this is the CreateNonGenericCollection method:

        private EntityCollection CreateNonGenericCollection(IEntityCollection2 genericCollection)
        {
            EntityCollection nonGenericCollection = new EntityCollection(genericCollection.EntityFactoryToUse);
            foreach (IEntity2 entity in genericCollection)
            {
                nonGenericCollection.Add((EntityBase2)entity);
            }
            return nonGenericCollection;
        }

I'm adding the entities contained in collection.RemovedEntitiesTracker to a new collection of type EntityCollection so that we always pass a non-generic collection rather than a generic collection.
Otis (LLBLGen Pro Team)   Posted on: 17-Nov-2007 10:58:12.
Ok, thanks for the info, that indeed narrows the focus where to look. The related entities in which hte crash occurs shouldn't be serialized at all, so there's a bug somewhere and I can now check the code to see if I made a mistake in implementing the algorithm Regular Smiley

Otis (LLBLGen Pro Team)   Posted on: 19-Nov-2007 11:01:23.
Reproduced:
Code:

/// <summary>
/// Test which checks if the entities which are tracked using a delete tracker,aren't serializing over XML their related entities.
/// </summary>
[Test]
public void XMLSerializationDeserializationOfDeleteTrackedEntitiesTest()
{
    EntityCollection<OrderEntity> orders = new EntityCollection<OrderEntity>();
    using(DataAccessAdapter adapter = new DataAccessAdapter())
    {
        PrefetchPath2 path = new PrefetchPath2(EntityType.OrderEntity);
        path.Add(OrderEntity.PrefetchPathOrderDetails);
        adapter.FetchEntityCollection(orders, new RelationPredicateBucket(OrderFields.CustomerId == "CHOPS"), path);
    }

    Assert.AreEqual(8, orders.Count);

    EntityCollection<OrderEntity> tracker = new EntityCollection<OrderEntity>();
    orders.RemovedEntitiesTracker = tracker;

    // delete the first 2 orders
    orders.RemoveAt(0);
    orders.RemoveAt(0);

    Assert.AreEqual(2, tracker.Count);

    // now serialize to XML the entities in the tracker.
    string xml = string.Empty;
    tracker.WriteXml(XmlFormatAspect.Compact25 | XmlFormatAspect.DatesInXmlDataType | XmlFormatAspect.MLTextInCDataBlocks, out xml);

    // deserialize the orders back into new entities
    EntityCollection<OrderEntity> deserializedOrders = new EntityCollection<OrderEntity>();
    deserializedOrders.ReadXml(xml);

    Assert.AreEqual(0, deserializedOrders[0].OrderDetails.Count);
    Assert.AreEqual(0, deserializedOrders[1].OrderDetails.Count);
}

Looking into it.
saggett (User)   Posted on: 19-Nov-2007 11:10:08.
That's good, I'm glad that the problem wasn't due to a problem in my code and that I've helped you find a bug.

Also, I was wondering - what does LLBLGen stand for?


saggett (User)   Posted on: 19-Nov-2007 11:15:31.
Okay, just looked it up myself (Lower Level Business Layer Gen). Regular Smiley
Otis (LLBLGen Pro Team)   Posted on: 19-Nov-2007 11:27:52.
saggett wrote:
That's good, I'm glad that the problem wasn't due to a problem in my code and that I've helped you find a bug.

Fixed in next build (11192007, released later today).

The issue was that Compact25 format expects that if an element is specified, it has a value, otherwise the element shouldn't be there, to keep the XML as small as possible. The References to m:1 and 1:1 related entities were still there as element (no data), e.g. <Customer/>

This triggered the XML deserializer to instantiate a new entity and deserialize the element data into that, which was empty and thus it crashed.

I'll attach a build to this post so you can continue with your work, after I've ran all the unittests again.
Attached.