Friday, September 9, 2011

WCF - SubClasses and KnownType

As part of the "Market Game" that I am working on I have been trying to expose a significant portion of the functionality through WCF services rather than just working with the "Service Layer" directly. While some functionality will only ever be exposed to internal systems (such as the AI logic), the user-facing functions could be accessed via a number of UIs including WP7 and HTML5, so exposing these functions via wcf is desirable.


As it turns out, testing via the WCF services was a really good thing, because I identified an interesting issue that took me quite a while to identify.

Example
In my example I have the following classes


Item – any object that can be bought or sold
BaseItem:Item – an object that has no "ingredients" (an "elemental object")
ComplexItem:Item – an object that is created from multiple other Items
Asset – an ownership record for an item (can be either a BaseItem or ComplexItem)
StockPile – a list of Assets for a particular user at a particular location

I then have a WCF service that returns a StockPile given a user and location (including the list of assets). This call was failing and I had no idea why, especially considering it did succeed before I made a few changes to the data model.

Troubleshooting
As we all know, debugging WCF errors can be tricky, so when I first encountered an error I had to try a number of different things to get this working (the errors was the trusty generic wcf "the underlying connection was closed" error).


The item I immediately thought of was the "maxItemsInObjectGraph", "maxReceivedMessageSize", and "maxBufferSize" binding settings as these had been encountered before when working with list data and returning 'complex' data types. The data I was returning wasn't that large, but I knew the default limits could be hit fairly easily.


When that failed I tried to find details on "100 Continue" messages (this popped up in a trace, and I had seen an issue like this before). I ended up forcing this off with "System.Net.ServicePointManager.Expect100Continue = false" but this also did not resolve the issue.



Solution
Finally, I stumbled upon a post mentioning serialisation of abstract types and the need for the KnownTypeAttribute which immediately triggered an old memory of having to do this back at my old work but with classic asmx web services.


Anyway, as it turns out, the serialisation process cannot serialise/deserialise derived types that are defined using their base type – e.g. "public Item getItems(){return new BaseItem();}" will fail. However, if you specify that the "KnownTypes" for Item are BaseItem and ComplextItem, then the serialiser can correctly inspect identify the actual Item type and serialise it appropriately.


"If you use a Base Class as the type identifier for a WCF method (parameter or return value) you must add a KnownTypes declaration to the base class DataContract"
Therefore the fix is to add the following to the Item class definition


[KnownType(typeof(BaseItem))]
[KnownType(typeof(ComplexItem))]
Public
class Item

WCF can then magically understand and serialise a List object graph correctly.

No comments:

Post a Comment