WCF deserialization creates base class instances

Posts   
 
    
parker
User
Posts: 2
Joined: 14-May-2008
# Posted on: 14-May-2008 23:53:35   

Hi there

I have a couple of entities that are all derived from the same entity:

Base class: ContactEntity Derived classes: Person, Company...

I implemented a custom entity factory to decide what kind of entity to create, which works like a charm - so far so good. However, the thing breaks in a WCF scenario - although the service returns an EntityCollection that contains different types, deserialization on the client side produces only _ContactEntity _instances.

Here's my service, which returns _Person _and _Company _instances:

public IEntityCollection2 GetContacts()
{
using (var adapter = data.CreateAdapter())
{
  //this creates different entitie
  EntityCollection col = new EntityCollection(new ContactFactory());
  adapter.FetchEntityCollection(col, null);

  return col;
}
}

I assume that the client side deserialization does not get the factory information that would be needed to create the correct types. XML serialization, however, works fine, because there is a hint to the factory included in the serialized output - the collection that performs the read doesn't even need to be constructed with a factory instance:

  EntityCollection col = new EntityCollection(new ContactFactory());
  adapter.FetchEntityCollection(col, null);

  //XML serialization works good - a serialization cycle produces
  //the correct entity types again
  string xml;
  col.WriteXml(out xml);

  col = new EntityCollection();
  col.ReadXml(xml);

Any idea where's the problem here?

Cheers, Philipp

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 15-May-2008 05:24:06   

Phillipp, what LLBLGen version are you using? Also, please post your Service or Data Contract.

I haven't faced this before, but I've heard about NetDataContractSerializer (soome kind of SharedType). I'll look around for some hints..

David Elizondo | LLBLGen Support Team
parker
User
Posts: 2
Joined: 14-May-2008
# Posted on: 15-May-2008 12:35:38   

Hi David

Thanks for the fast reply (and sorry for the missing specs)! I'm using LLBLGen 2.5 with Adapter. The service itself is very rudimentary (I just implemented it for testing purposes), so I'll just paste it here:

namespace SomeCompany.Contacts.Services.Data
{
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
    Namespace = "http://www.somecompany.ch/contacts/")]
  public class EntityProvider : IEntityProvider
  {
    private readonly DataManager data = new DataManager(@"C:\somedatabase.fdb");


    //This service works ok - the correct entity type is being deserialized on the
    //client side
    public ContactEntity TryGetContact(int contactId)
    {
      using (var adapter = data.CreateAdapter())
      {
        LinkedContact contact = new LinkedContact(contactId);
        IPrefetchPath2 path = new PrefetchPath2(EntityType.ContactEntity);
        path.Add(ContactUtil.GetDefaultContactAttributesPath());
        bool status = adapter.FetchEntity(contact, path);

        return status ? contact : null;
      }
    }


    //gets all contacts from the database, the custom factory handles
    //correct type creation (returned types are Company, Person etc.)
    public IEntityCollection2 GetContacts()
    {
      try
      {
        DateTime dt = DateTime.Now;

        using (var adapter = data.CreateAdapter())
        {
          EntityCollection col = new EntityCollection(new ContactFactory());
          adapter.FetchEntityCollection(col, null);     
          return col;
        }
      }
      catch (Exception e)
      {
        Console.WriteLine(e);
        return null;
      }
    }
  }
}

Here's the service declaration:

  <system.serviceModel>

    <services>

      <!-- expose a single data service -->
      <service name="Dialogik.Contacts.Services.Data.EntityProvider" behaviorConfiguration="ServiceBehavior">

        <endpoint contract="Dialogik.Contacts.Services.Data.IEntityProvider" address="TcpEntityProvider" binding="netTcpBinding"/>

        <endpoint contract="IMetadataExchange" address="mex" binding="mexHttpBinding"/>

        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8000"/>
            <add baseAddress="http://localhost:9000"/>
          </baseAddresses>
        </host>

      </service>
    </services>

    <behaviors>
    
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

Here's the client configuration:

  <system.serviceModel>

    <bindings>
      <netTcpBinding>
        <binding name="defaults" maxReceivedMessageSize="15655360" />
      </netTcpBinding>
    </bindings>
    
    <client>
      <!-- TCB binding -->
      <endpoint address="net.tcp://localhost:8000/TcpEntityProvider" binding="netTcpBinding"
                bindingConfiguration="defaults"
        contract="Dialogik.Contacts.Services.Data.IEntityProvider" name="TcpService"
                  />
    </client>

  </system.serviceModel>

and finally the client code:

static void Main(string[] args)
{
  ChannelFactory<IEntityProvider> factory = new ChannelFactory<IEntityProvider>("TcpService");
  IEntityProvider service = factory.CreateChannel();

  //get the contacts via the service
  var contacts = service.GetContacts();

  //this listing shows that all contacts are instances of the base entity class (the factory
  //was not invoked)
  foreach (ContactEntity contact in contacts)
  {
    Console.Out.WriteLine("Contact of type {0}: {1}", contact.GetType(), contact.ContactName);
  }
}

Another observation - I spoofed network communication and the factory is indeed transmitted over the wire, but not used by the client when it comes to desrialization. Here's the transmission:

<EntityCollection xmlns="http://schemas.datacontract.org/2004/07/Dialogik.Contacts.Data.Generated.HelperClasses"><EntityCollection Factory="Dialogik.Contacts.Data.Contacts.ContactFactory, Dialogik.Contacts.Data" Format="Compact25"><ContactEntity ObjectID="ece02a0b-0765-49da-aaeb-ec3d32ba5e50"><ContactId>4</ContactId><ContactTypeId>1</ContactTypeId><ContactName>Anton Muster</ContactName><Attributes><_lps f="7"/></Attributes><MainAttributes>System.Linq.Enumerable+&lt;WhereIterator&gt;d__0`1[Dialogik.Contacts.Data.Generated.EntityClasses.ContactAttributeEntity]</MainAttributes><_lps fs="AA==" es="1"/></ContactEntity><ContactEntity ObjectID="fd707afa-8d6e-4a14-98d6-1f0b5fce9a42"><ContactId>5</ContactId><ContactTypeId>1</ContactTypeId><ContactName>Beatrice Muster</ContactName><Attributes><_lps f="7"/></Attributes><MainAttributes>System.Linq.Enumerable+&lt;WhereIterator&gt;d__0`1[Dialogik.Contacts.Data.Generated.EntityClasses.ContactAttributeEntity]</MainAttributes><_lps fs="AA==" es="1"/></ContactEntity><ContactEntity ObjectID="d04e73df-0ee7-4ad4-996d-6456269080d0"><ContactId>6</ContactId><ContactTypeId>3</ContactTypeId><ContactName>A. &amp; B. Muster</ContactName><Attributes><_lps f="7"/></Attributes><MainAttributes>System.Linq.Enumerable+&lt;WhereIterator&gt;d__0`1[Dialogik.Contacts.Data.Generated.EntityClasses.ContactAttributeEntity]</MainAttributes><_lps fs="AA==" es="1"/></ContactEntity><ContactEntity ObjectID="38fa8eef-7589-49c5-b90a-2e412cb3b749"><ContactId>7</ContactId><ContactTypeId>2</ContactTypeId><ContactName>Treuhand AG</ContactName><Attributes><_lps f="7"/></Attributes><MainAttributes>System.Linq.Enumerable+&lt;WhereIterator&gt;d__0`1[Dialogik.Contacts.Data.Generated.EntityClasses.ContactAttributeEntity]</MainAttributes><_lps fs="AA==" es="1"/></ContactEntity><ContactEntity ObjectID="d4bcf732-1753-4e73-bed7-3c68404f458a"><ContactId>8</ContactId><ContactTypeId>2</ContactTypeId><ContactName>UBS</ContactName><Attributes><_lps f="7"/></Attributes><MainAttributes>System.Linq.Enumerable+&lt;WhereIterator&gt;d__0`1[Dialogik.Contacts.Data.Generated.EntityClasses.ContactAttributeEntity]</MainAttributes><_lps fs="AA==" es="1"/></ContactEntity><_lps f="7"/></EntityCollection></EntityCollection>
Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 15-May-2008 15:34:00   

Please check the following link to know how to get the specific runtime library version: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7722

Did you specify a ServiceKnownType attribute to the service contract specifying the types you do return.

eg.

    [ServiceContract()]
    [ServiceKnownType(typeof(CustomersEntity))]

Please follow the WCF-Example posted on the download section of LLBLGen website.