views:

48

answers:

2

How would I tell the WCF service what KnownTypes to use when passing data back to the client?

I know I can use the [ServiceKnownType] attribute, which makes the service call run fine from a WCF Test Server, however it still fails from the client. Am I missing something here?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

Error message from client is:

{"Element 'http://schemas.datacontract.org/2004/07/BaseClassZ' contains data from a type that maps to the name 'http://schemas.datacontract.org/2004/07/SubClassA'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'SubClassA' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer."}

Serializing/Deserializing the object on the WCF server using a DataContractSerializer and a list of KnownTypes works fine.

UPDATE: It appears I can get the client to read the object correctly if I add KnownType attributes to the base class, but I am still looking for a way around this if possible since the base class is used for a lot of items and I don't want to have to modify the KnownType attributes on the base class anytime I add a new item.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}
A: 

There is another way to do this. Rather than using "add service reference" you code the proxy classes. It is a little more coding initially but gives you a much more stable and robust solution. We have found that this saves us time in the long run.

See: http://www.dnrtv.com/default.aspx?showNum=122

Note: this only works if you have control of both the server and the client.

Shiraz Bhaiji
A: 

To avoid deterring your service code put the known types into web.config of the service:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

If you want to do it by code you need to use this attribute on the service interface and not on the operation method but I would prefer the declarative approach:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}
Darin Dimitrov