WCF Tutorial - Serializing and Transmitting Base Types

Skill

WCF Tutorial - Serializing and Transmitting Base Types

Posted in:

By default, you cannot transfer an object as its base type between a WCF server and client. The DataContractSerializer will throw an exception when it attempts to serialize the base class, but instead finds your derived class. In this tutorial, I'm going to show you how to add attributes to your base class that will allow this functionality to exist.

In my example, I'm going to create a pretty standard object hierarchy. Person <- Employee <- Manager.

public class Person
{
  public string Name { get; set; }
}

public class Employee : Person
{
  public string EmployeeID { get; set; }
}

public class Manager : Employee
{
  public List<Employee> Employees { get; set; }
}

As you can see, Person is the base class, Employee derives from Person, and Manager derives from Employee. In my example WCF service contract, I want to send a collection of all the people. The easiest way to do this would be to send a List of the base type, Person.

Person person = new Person();
person.Name = "Person";

Employee employee = new Employee();
employee.Name = "Employee";

Manager manager = new Manager();
manager.Name = "Manager";

List<Person> people = new List<Person>()
{
  person,
  employee,
  manager
};

In order to simulate sending this collection, I'm going to directly invoke the DataContractSerializer, which is what WCF uses when serializing objects.

DataContractSerializer testSerializer = new DataContractSerializer(typeof(List<Person>));
using (MemoryStream stream = new MemoryStream())
{
  testSerializer.WriteObject(stream, people);
}

If we execute this code, we'll receive the following exception:

Type 'WCFSerializationErrors.Employee' with data contract name 'Employee:http://schemas.datacontract.org/2004/07/WCFSerializationErrors' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

We got this exception because the DataContractSerializer was excepting a Person object and we gave it an Employee object. We have to tell the serializer about other known types before they can be serialized correctly. We do this through the KnownTypeAttribute. These attributes should be applied to your base class.

[KnownType(typeof(Employee))]
[KnownType(typeof(Manager))]
public class Person
{
  public string Name { get; set; }
}

Now the serializer recognizes the types Employee and Manager, and will serialize them when passed as their base type. That's all we have to do. If we attempt to serialize the data again, everything will work as expected. Hopefully this tutorial shed some light on the DataContractSerializer and using this trick can help reduce the complexity and increase the cleanliness of WCF service contracts.

stapia.gutierrez
04/14/2010 - 08:58

Hey nice little guide. Just in time as well, because I'm just learning how to use WCF. :D

reply

David Priebe
04/14/2010 - 16:51

Is there a reason not to use data contract? I have a some simple data contract structures that do not seem to need the KnownType tag to work. With the structures I'm working in, having the base type know about all needed sub-types is not a viable structure. After all, anyone could inherit from person in a different assembly and person may never know about it (and shouldn't know about it).

reply

The Reddest
04/14/2010 - 19:27

In my example, the use of DataContract is optional. By default, the DataContractSerializer will serialize every public property. Using DataContract and DataMember can help fine-tune what members you'd like serialized.

The DataContractSerializer will have to know about every type that inherits it if you want to be able to serialize and transfer it to clients. This makes sense, because on the receiving end, the client will have to know about the same types if it wants to be able to deserialize it correctly.

If you extend Person in another assembly and attempt to send it to the client, how will the client know how to turn it back into the correct derived type if it's not aware of the assembly and the type?

reply

David Priebe
04/15/2010 - 08:23

I realized that my case is not exactly on point. In my case I'm mostly returning the sub-class rather than the base class at which point the base classes are (obviously) discoverable.

Still the core issue remains. The base class should not need to know about the subclasses. Who inherits from it is none of it's business. I would prefer to be able to decorate the method returning the base class with all the sub classes that are of interest (similar to ASP .Net web services). Or to be able to list the base classes in a .config file.

reply

The Reddest
04/15/2010 - 08:58

I definitely agree that the base types should not have to know about the derived types. You can specify the known types in the app config file. If you're doing it in code, however, you'll have to use the attributes.

reply

David Priebe
04/15/2010 - 09:17

Thanks for the link on that! Looks like lots of options in there.

reply

Claudioe
07/10/2010 - 07:46

Hi,

If you agree to loose interoperability (for instance you have control on client and services) and you don't share things with external systems, you can get rid of these problem using NetDataContractSerializer.
There is a nice post, that still works in framework 4

http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx

we are using this solution since years and it works like a charm.
Hope this helps you-

Claudio

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors