My WCF service exports a single operatio开发者_Go百科n, marked with the catch-all action and reply action so that it represents a common entry point to the service:
[ServiceContract]
public interface IService
{
[OperationContract (Action="*", ReplyAction="*")]
Message MyMethod (Message msg);
}
Client proxies are still generated as data contracts.
What I'm finding, however, is that despite the client sending a data contract, when msg
is serialized, the body appears to be the equivalent message contract to the data contract, not the data contract itself.
Even this is fine, except that extracting the data contract inside involves doing a manual parsing of the incoming XML. The service itself does not have an actual MessageContract
type to use, so accessing the body means extracting nodes, relabeling elements, and so on. It's a manual process for something that, presumably, WCF is already handling under the covers when the exposed operations are not Message
-based.
How does WCF do this when it's data contract-to-data contract? Is there a way that I can use that same process?
That is the correct default behavior. Each time a request or a response data are send they are automatically wrapped in the wrapping element. It is also known as Wrapped parameter style. If you don't want to use it and instead you want to use Bare parameter style you have to define message contract and set its IsWrapped property to false. Like this simple example:
[ServiceContract]
public interface IService
{
[OperationContract]
GetMessageResponse GetMessage(GetMessageRequest request);
}
[MessageContract(IsWrapped = false)]
public class GetMessageResponse
{
[MessageBodyMember]
public string Result { get; set; }
}
[MessageContract(IsWrapped = false)]
public class GetMessageRequest
{
[MessageBodyMember]
public string Data { get; set; }
}
GetMessage operation will not use wrapping in request and response.
Limitation is that operation has to accept only single MessageContract as the parameter and always has to return MessageContract (even if it returns void). So the easiest way to achieve your requirement is to convert all your data contracts to message contracts by replacing attributes.
Another way is to create separate message contract for each request and response and use properties of your data contracts types as message body. If for any reason you don't like the idea of creating two additional message contracts for each operation and you still want to preserve old data contracts you can use little hack (I don't see any reason for using it but it works). Add MessageContract attribute to your data contracts and MessageBodyMember attribute to all your data members.
[DataContract, MessageContract(IsWrapped = false)]
public class MyData
{
[DataMember, MessageBodyMember]
public string Data { get; set; }
}
I agree with Ladislav that what you're seeing is the correct behavior. There's a great document on MSDN that describes what's happening when WCF receives and sends messages:
http://msdn.microsoft.com/en-us/library/aa347789.aspx
The key paragraph that pertains to what you're asking is here:
Reading the message is primarily used by the service framework when receiving messages. For example, when the DataContractSerializer is in use, the service framework will get an XML reader over the body and pass it to the deserialization engine, which will then start reading the message element-by-element and constructing the corresponding object graph.
So the framework uses a DataContractSerializer
to deserialize the payload (which is an XML infoset) to the appropriate class when it's received. You could attempt to use the same logic - by extracting the payload (probably using Message.GetReaderAtBodyContents()
which returns an XmlReader) and then using the DataContractSerializer to deserialize the XML to the object that you want to work with with the returned XmlReader
.
Hopefully this helps!
精彩评论