Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Client calling method with polymorphic array fails

I have a WCF service with the following (example) interface:

[ServiceContract]
[ServiceKnownType(typeof(Foo))]
[ServiceKnownType(typeof(Bar))]
[ServiceKnownType(typeof(Baz))]
public interface IMyInterface
{
    [OperationContract]
    void ProcessMessage(Message message);

    [OperationContract]
    void ProcessMessages(IEnumerable<Message> messages);
}

Foo, Bar and Baz are all a type of Message.

I can call ProcessMessage() from a WCF client with either a Foo, Bar or Baz object and everything works fine. I can't, however, call ProcessMessages(...) with an array (or list or any other IEnumerable) because this will fail:

There was an error while trying to serialize parameter http://tempuri.org/:messages. The InnerException message was 'Type 'X.X.Foo' with data contract name 'Foo:http://schemas.datacontract.org/2004/07/X.X' 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.'. Please see InnerException for more details.

When I take a look at the generated client code, reference.cs, I see:

...
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyInterface/ProcessMessage", ReplyAction="http://tempuri.org/IMyInterface/ProcessMessageResponse")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Foo))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Bar))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Baz))]
void ProcessMessage(X.X.Message message);

...
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyInterface/ProcessMessages", ReplyAction="http://tempuri.org/IMyInterface/ProcessMessagesResponse")]
void ProcessMessages(X.X.Message[] messages);

I notice the ServiceKnownTypeAttributes are added to ProcessMessage but not ProcessMessages. When I manually add the same set of ServiceKnownTypeAttributes to the ProcessMessages method and I call it from the client with an array containing a Foo, Bar and Baz it works fine.

How do I get Visual Studio to generate these attributes on the second method too? Is my IMyInterface wrong? Did I add the [ServiceKnownType(typeof(...))] in the wrong place? Where did I go wrong?

Edit I should, maybe, mention that the Message class is in an "external" assembly (which I can control, luckily) which I packaged in a Nuget package which, in turn, is referenced in both the WCF service and client with the "reuse types..." option enabled for this assembly.

like image 735
RobIII Avatar asked Sep 13 '26 10:09

RobIII


1 Answers

Since your types are in different assemblies, and/or it's hard to modify Message, you can put this in the client assembly to make it work:

namespace X.X
{
    [KnownType(typeof(Foo))]
    public partial class Foo
    {
    }
    [KnownType(typeof(Bar))]
    public partial class Bar
    {
    }
    [KnownType(typeof(Baz))]
    public partial class Baz
    {
    }
}

(I know it seems like you're telling it the bleedingly obvious, but it works.)

If Message, Foo, Bar, and Baz were in the same assembly, you could just add the KnownType attribute to your Message class:

[KnownType(typeof(Foo))]
[KnownType(typeof(Bar))]
[KnownType(typeof(Baz))]
public abstract class Message

Then you wouldn't have to do anything special to your clients.

Also, the only way I was able to reproduce your behavior was by putting Message in its own assembly, referencing that from both my client and WCF library projects, and using the "Reuse types in referenced assemblies" checkbox. If the classes are all generated on the client side, instead of some being referenced and some generated, it works much more simply, because it generates all the KnownType attributes on Message.

like image 91
Tim S. Avatar answered Sep 15 '26 23:09

Tim S.