Search code examples
wcfc#-4.0datacontractmessagecontractknown-types

KnownType for MessageContract in WCF


I am using Stream object inside my wcf Contracts so forced to use MessageContract instead of DataContract.

 [MessageContract]
    public class Document 
    {
        [MessageBodyMember(Order = 1)]
        public System.IO.Stream FileData;

}

 [MessageContract]
    public class A : Document 
    {
        [MessageBodyMember]
        public string input;

}

 [MessageContract]
    public class B : Document 
    {
        [MessageBodyMember]
        public string someProp;

}

[ServiceContract]
    public interface ISomeService
    {

        [OperationContract]
        Document SomeMethod(Document file);
}

I want the consumer of this service to create object of either A or B and call the service with it. On the service side, I can type cast it to proper object then perform some action.

Problem is I cannot specified KnownType with MessageContract and inherited contracts cannot be exposed to client until they are used in service or declared with KnownType.

I tried google it but couldn't find anything related to KnownType with MessageContract.

As suggested in comment... i updated my message contract with KnownType but they are still not exposed to client through service reference...

[MessageContract]
    [KnownType(typeof(FileSystemStoredDocument))]
    [KnownType(typeof(FileBoundStoredDocument))]
    [KnownType(typeof(SharepointStoredDocument))]

    public class Document : DocumentInfo, IDisposable
    {
}

Can any one help me what's wrong here?

Note: ALL KnownType are inherited from Document


Solution

  • Message contracts describe exactly how the message should look like. They do support inheritance, but you must specify the exact message contract you're using in a specific operation.

    If you check the body parts of the message:

    ContractDescription.GetContract(typeof(ISomeService)).Operations[0].Messages[0].Body.Parts
    

    You'll see exactly one part - a Stream object. That's in contrast to data contracts, where the body contains a part of the type Object. So you see why KnownType wouldn't work here.

    (The ContractDescription class is used, among other things, to generate WSDL. See the WsdlExporter class.)

    What you can do is create a hierarchy of data contracts that would be contained in the message contract, e.g.

    [MessageContract]
    public class Document 
    {
        [MessageHeader]
        public DocumentProperties Properties;
    
        [MessageBodyMember(Order = 1)]
        public System.IO.Stream FileData;
    }
    
    [DataContract]
    [KnownType(typeof(A))]
    [KnownType(typeof(B))]
    public abstract class DocumentProperties { }
    
    [DataContract]
    public class A : DocumentProperties 
    {
        [DataMember]
        public string input;
    }
    
    [DataContract]
    public class B : DocumentProperties 
    {
        [DataMember]
        public string someProp;
    }
    

    Note that you you cannot have more than one body member if you want to pass a Stream, so the rest of the properties must be in headers.