views:

859

answers:

5

Hi there,

I am fairly new to WCF and just have a question on how to correctly get MessageContract inheritance working. A simplified version of my setup is as follows - a "base" message type, and then another "test" message which inherits from it.

[MessageContract]
public abstract class BaseMessage
{ }

[MessageContract]
public class TestMessage : BaseMessage
{ }

I then have an asynchronous OperationContract on a ServiceContract defined as:

[OperationContract(AsyncPattern = true)]
IAsyncResult BeginFindRequest(BaseMessage request, AsyncCallback callback, object asyncState);

The problem that I am getting is when calling the BeginFindRequest method, and passing in a TestMessage instance for the request parameter, the WCF framework is deserialising the TestMessage instance to BaseMessage on the service/server side. As this is defined as an abstract class, it results in the following error:

"The message cannot be deserialized into MessageContract type BaseMessage since it does not have a default (parameterless) constructor."

From the limited information that I can find on MessageContract inheritance, it seems that it should just work.

So my question is - what am I missing in order to get this to work; or should I perhaps rather define a seperate OperationContract on the ServiceContract specifically for that type - the downside being that I could end up with many additional OperationContracts?

Thanks in advance for any assistance...

A: 

Is that possible to change BaseMessage so that it is concrete class with parameterless constructor?

The error message tells that there is no way to initialize the object of type BaseMessage because it is abstract.

codemeit
I have tried that, but then the message is deserialised into that type, therefore losing the TestMessage type information.The BeginFindRequest currently applies logic based on the type of the message, meaning that the logic does not execute as the type is then BaseMessage.
Frank Bell
+2  A: 

OK, first question is: why are you really using Message contracts? Do you really have a need for that??

Typically, message contracts are only ever used when you need to tightly control the layout of your SOAP message, e.g. to satisfy a legacy system you need to call which requires specific headers and such.

A "normal" WCF call should hardly ever need to use a message contract.

You define your service calls (the methods on your service) using [ServiceContract], and the data structures being passed around as [DataContract]. If you have a DataContract, you have more options as to how to deal with inheritance / polymorphism in your service (more than with the message contract construct).

Marc

marc_s
The reason that I went for MessageContract was due to reading a WCF best practices article which as I recall, suggested that using them over DataContracts provided less message "bloat". I am trying to find the article to reference now.I will try converting to DataContract to see if that immediately solves the problem. Thanks Marc
Frank Bell
http://www.designpatternsfor.net/default.aspx?pid=99 - see from Listing 5 onwards (Service Message Pattern).
Frank Bell
Thanks for the link; the point that Rob is making in this article seems a bit "too advanced" for my taste. In most cases, you won't really care about this "double-wrapping" that he criticizes. Again: if you really MUST control the exact message layout, then YES - use message contracts. But for me, that's a LAST RESORT kind of approach, if nothing else works. I wouldn't recommend that as a default practice.
marc_s
A: 

The error simply wants you to have a default empty contructor that it can use. However, I agree with marc_s; in the projects I've worked on I've rarely used message contract, the only case I can remember was as part of a file transfer service where file chunks were passed in messasges.

Tanner
As per my comment to codemeit, removing the abstract keyword enables the deserialisation to work, but I am then left with a BaseMessage object when I should have a TestMessage object.I am going to convert to DataContract to see if it resolves the issue.
Frank Bell
Can you keep it abstract, but yet give BaseMessage a protected default constructor?
Zach Bonham
A: 

Try decorating your [ServiceContract] with the KnownType attribute. Since TestMessage is not 'visible' from a public operation, this helps the plumbing know how to treat it when it sees it.

If this should allow the [DataContract] to be serialized as a TestMessage your still likely to need to handle multiple messages differently via 'is a' or some other casting.

Zach Bonham
Have tried the KnownType and ServiceKnownType attributes which has not helped, but as I am using the NetDataContractSerializer this should not be required?
Frank Bell
Interesting..no, you shouldn't need KnownType/ServiceKnownType as I understand it, but that also implies that your sharing a common dll somewhere with the TestMessage declared in it if not using KnownType/ServiceKnownType? Otherwise, the client wouldn't know anything about TestMessage, only BaseMessage - just hypothesizing here as my experience with NetDataContractSerializer is nil - I may have to look harder to find a need for it to get some experience! :) How are you replacing the default DataContractSerializer? Are you replacing it before the service is open for business?
Zach Bonham
Yup, all required libraries are being shared between server/client. In terms of replacing the DataContractSerializer, I went for a factory method type approach over the various Attribute-based solutions available - so that I didnt have to place even more attributes everywhere. This basically returns a servicehost/proxy with all DataContractSerializerOperationBehaviors replaced with my own NetDataContractSerializerOperationBehaviour - which provides a NetDataContractSerializer when required.
Frank Bell
+2  A: 

In the end I found this blog post which hit the nail on the head -

Unfortunately the way that contracts are expressed in WCF makes is very easy to forget what their purpose is: to define the messages send to the operation and being sent back from the operation. In reality you have to think “how would I express this data in XML?”. XML doesn’t support inheritance so whatever you put in the contract is going to have to have some way of mapping to XML. The data contracts used to define the messages are simply a .NET typed convenience for generating the XML for the data you want to pass – if you view them any other way you are destined for a world of pain. So think about the data you want to pass, not how it may happen to be represented in your business layer and design your DataContracts accordingly.

http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,a3775eb1-b441-43ad-b9f1-e4aaba404235.aspx

So I will be refactoring to provide an additional method with an explicit contract type. This will also allow me to clean up the service implementation by removing all the type checking.

Thanks for the assistance.

Frank Bell
Yes, yes - the "OO" world and the "SOA" world are quite different beasts at times..... XML based messaging doesn't lend itself well to inheritance and other OO-isms that we've grown so accustomed to....
marc_s