Search code examples
.netsilverlightwcf-ria-services

Service method returning list of base objects gives me 'Complex types cannot specify a KnownTypeAttribute' message


I am using WCF RIA Services to return a custom data-transfer object to a Silverlight client.

I would like to return an object, say FruitBasket (similar to this post), that contains a list of Fruits. The Fruit class is a base class for derived classes, such as Apple, Pear, Peach, etc. At runtime, the list of Fruits could contain any of those derived types. I want RIA Services to serialize and deserialize the whole FruitBasket object, including its collection of Fruits.

  • If I just define my class hierarchy and return the entire FruitBasket object, I get a runtime error:

Type 'Peach' with data contract name 'Peach:http://schemas.datacontract.org/2004/07/FruitApp' 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.

  • If I then specify the [KnownType(typeof(Peach))] attribute on my Fruit class, I get a compile time error:

Complex type 'Fruit' is invalid. Complex types cannot specify a KnownTypeAttribute.

How can I set up my data model so that I can access the Fruit collection on the client? On the client side, it would even be ok if the objects are recognized only as "Fruit" objects. I just need them to deserialize.


Solution

  • RIA Services support 2 kinds of user types: complex types and entity types. An entity is a persistent object that has a key that uniquely identifies it. A complex type is simply a grouping of several primitive type fields into a single object. They don't work with the generic concept of classes and hierarchies, they put restrictions on what you can or cannot do in order to solve some specific problems more efficiently.

    One of the restrictions is that complex types cannot have derived types. It's difficult to say why, but I suppose that without the restriction the framework would be much more complicated.

    Now, there are several options to solve the issue:

    1. If you already have an appropriate key in your Fruit class (say, a database Id), just mark it with [Key] attribute.
    2. If you don't have a key and you don't care, add a Guid property that returns Guid.NewGuid(), mark it with [Key] and you'll be fine. Note that in this case you will not be able to set up any relations to Fruit entities, as well as save changes back to the server.
    3. Do not use inheritance. If it's not really necessary, why not live without it?
    4. Use plain WCF. RIA Services are not a generic-case communication framework. They are highly specific for all these entities-relations stuff.

    The choice is up to you cause it depends on your requirements.