DataContract Serialization, Entity Framework and "Known Types"

One of the challenges encountered when using DataContract serialization (the default for WCF web services) is support for polymorphic types.  If you have a type hierarchy (eg. class A and class B which inherits from A) and each class in the hierarchy has DataContract/DataMember attritibutes, then you can create service methods that take those types as parameters or return them, BUT the passed in or returned types must exactly match the types in the operation signatures.  By default you cannot pass an instance of B as a parameter to an operation whose signature says that parameter has type A (even though B "is a" A).

The fix for this problem is called "known types".  There are a few different mechanisms (you can learn more about them from this great blog post, but the general idea is to provide the serializer knowledge of the additional types so that it knows how to serialize and deserialize them.

Unfortunately in beta 3 and previously, the automatically generated code for EF models does the naive thing and just describes the data contracts for each type without providing the additional information about the derived types.  We're looking into updating code generation in a future release to make this work out of the box, but in the meantime if you want to build a WCF service which exchanges entity type instances, you need to make the system aware of those types.

This afternoon I cooked up the following code for that purpose which I thought might be useful to others.  The basic idea is to add a static method to the partial class which will use the EF metadata system to determine the list of all CLR types which represent entities in the assembly containing the context--typically this would include all of the entities in your model.  Then you add an attribute to either the service operation or the whole service contract pointing to that static method.

private static IEnumerable<Type> _entityTypes = null;

public static IEnumerable<Type> GetKnownEntityTypes(ICustomAttributeProvider provider)

{

    if (_entityTypes == null)

    {

        var objectItemCollection = new ObjectItemCollection();

        objectItemCollection.LoadFromAssembly(Assembly.GetExecutingAssembly());

        _entityTypes = from entityType in objectItemCollection.GetItems<EntityType>()

                       select objectItemCollection.GetClrType(entityType);

    }

    return _entityTypes;

}

The attribute on the service contract (or service method) would look something like this (where DPMudDB is replaced with the strongly typed context name for the model you want to use):

[ServiceKnownType("GetKnownEntityTypes", typeof(DPMudDB))]

Once you do these two things, you can create service methods which send and receive not only derived types but even things typed only as "object" as long as the actual instances are entity classes from your model.

- Danny