yas4891
yas4891

Reputation: 4862

WCF: Use custom class in callback

I've managed to get WCF callback (over NamedPipes) working with strings, but I sadly can't get it to work with custom classes.

Here's the service declaration + implementation: http://pastebin.com/Zayi2kjT

here is my data contract: http://pastebin.com/wFCxrRcJ

here is what I'm doing on the client side: http://pastebin.com/jxEbyEtP

When I change

    public void Send()
    {
        try
        {
            channel.OnCallback("I love deadlines. I like the whooshing sound they make as they fly by.");
        }
        catch (Exception ex)
        {
            Form1.AddText(ex.GetType() + "|" + ex.Message);
        }
    }

to

channel.OnCallback(new TransmissionClass("I love deadlines. I like the whooshing sound they make as they fly by."));

I get the following exception on the server when invoking the callback method(see above):

System.ServiceModel.FaultException
Der Formatierer hat beim Deserialisieren der Nachricht eine Ausnahme ausgelöst:   
Fehler beim Deserialisieren von Parameter http://tempuri.org/:aUpdatedObject.  
Die InnerException-Nachricht war "Das Element "http://tempuri.org/:aUpdatedObject"  
enthält Daten eines Typs, der dem Namen "http://schemas.datacontract.org/2004/07
/ServerApp:TransmissionClass" zugeordnet ist.   
Dem Deserialisierungsprogramm ist kein Typ bekannt, der diesem Namen zugeordnet ist.  
Verwenden Sie ggf. einen DataContractResolver, oder fügen Sie den entsprechenden Typ für "TransmissionClass" der Liste der bekannten Typen hinzu.  
Verwenden Sie dazu z. B. das Attribut "KnownTypeAttribute", oder fügen Sie den Typ der an DataContractSerializer übergebenen Liste von bekannten Typen hinzu.".  
Weitere Details finden Sie unter "InnerException".

However, I have applied the KnownTypeAttribute to the WCF service implementation.

Upvotes: 3

Views: 1989

Answers (1)

anton.burger
anton.burger

Reputation: 5706

In general, whoever (server or client) is deserialising a parameter or return value needs to know what types (rather, what contracts) to expect at runtime in order to do so. In this case, a declared parameter of type object doesn't give the client enough information to deserialise whatever the server might be sending back to it. Replacing it with a type supported by the DataContractSerializer (and decorated with [KnownType], if the server plans to send subclass instances in the callback) solves this problem.

You can also use configuration to tell the DataContractSerializer about known types, but this is application-wide and seems like a bit of a blunt instrument to me.

I had an obscure case once where my contracts looked something like this:

[ServiceContract]
interface ISearch
{
    [OperationContract]
    SearchResult Search(SearchQuery query);
}

[DataContract]
[KnownType(typeof(Subclass1InSharedAssembly)), etc...]
class SearchResult
{
    [DataMember]
    BaseClassDeclaredInSharedAssembly Value { get; set; }
}

Because I wanted the server and client to use the (useful) shared assembly types, I had a reference to the shared assembly in both and chose not to generate any proxy types for them... and so svcutil didn't generate any KnownTypeAttributes for me. Partial classes saved my bacon; On the client, you can do this:

// generated proxy .cs
[DataContract]
partial class SearchResult
{
    ...
}

// hand-written .cs
[KnownType(typeof(Subclass1InSharedAssembly)), etc.]
partial class SearchResult
{
    // Now the client knows how to deserialise derived types.
}

Upvotes: 3

Related Questions