I've acquired more information since the original post and have been able to recreate the problem in a very simple File > New Project > WCF Service Application solution. As a result I'm heavily editing the original content of this post to get rid of some of the superfluous information and simplify the examples:
We have a message contract defined as follows.
[MessageContract( WrapperName = "SingleTypeResponse", WrapperNamespace = "urn:WcfService1" )]
public class SingleTypeResponse<T>
{
[MessageBodyMember( Name = "ReturnValue" )]
public T ReturnValue { ge开发者_StackOverflow社区t; set; }
}
The service interface has the following:
[OperationContract]
SingleTypeResponse<string> GetStringData();
[OperationContract]
SingleTypeResponse<int> GetIntData();
When I run the project and navigate to the .svc file I get the following:
An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior contract: http://tempuri.org/:IService1 ----> System.InvalidOperationException: The WcfService1.IService1.GetIntData operation references a message element [urn:WcfService1:SingleTypeResponse] that has already been exported from the WcfService1.IService1.GetStringData operation. You can change the name of one of the operations by changing the method name or using the Name property of OperationContractAttribute. Alternatively, you can control the element name in greater detail using the MessageContract programming model.
If I comment out GetIntData on the interface and test the service using the WCF Test Client, with the WrapperName property set, I get the following response XML:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<SingleTypeResponse xmlns="urn:WcfService1">
<ReturnValue xmlns="http://tempuri.org/">foo</ReturnValue>
</SingleTypeResponse>
</s:Body>
</s:Envelope>
I'm guessing that element is the source of the problem, it sees two versions of the same element, one with a string and one with an int.
I then uncommented the GetIntData and removed the WrapperName property from the MessageContract:
[MessageContract( WrapperNamespace = "urn:WcfService1" )]
public class SingleTypeResponse<T>
{
[MessageBodyMember( Name = "ReturnValue" )]
public T ReturnValue { get; set; }
}
I get the same error message with the exception that the message element it's complaining about is the ReturnValue property of the contract rather than the message contract name.
Once again commenting out GetIntData and testing with WCF Test Client I get:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<SingleTypeResponseOf_String xmlns="urn:WcfService1">
<ReturnValue xmlns="http://tempuri.org/">foo</ReturnValue>
</SingleTypeResponseOf_String>
</s:Body>
</s:Envelope>
So I'm able to get it to create a unique name for the wrapper, but the fact that SingleTypeResponseOf_String and SingleTypeResponseOf_Int32 both have a ReturnValue property continues to cause it to blow its brains out.
I've never thought about it before but it makes sense that generic types need to be defined as concrete types for a WSDL. Coming from that perspective, it appears that yes - you are defining two 'types' of SingleTypeResponse
in your service. WSDL does not allow two element definitions of the same name & namespace to coexist (for obvious reasons - they need to be uniquely identifiable).
There are a couple of potential conflicts here. I think you've identified the first one - an element named SingleTypeResponse
within the urn:WcfService1
namespace. When you turn off the naming of the message contract (allowing it to be named by the serializer) you see the below:
<!-- Setting wrapper name & wrapper namespace -->
<wsdl:message name="SingleTypeResponseOf_String">
<wsdl:part name="parameters" element="q1:SingleTypeResponse"
xmlns:q1="urn:WcfService1" />
</wsdl:message>
<!-- Setting wrapper namespace only -->
<wsdl:message name="SingleTypeResponseOf_String">
<wsdl:part name="parameters" element="q1:SingleTypeResponseOf_String"
xmlns:q1="urn:WcfService1" />
</wsdl:message>
This should avoid the first conflict, because running your two operations means that you'll be able to have two types of element (SingleTypeResponseOf_String
and SingleTypeResponseOf_Int
).
I believe your second conflict comes from the MessageBodyMember
attribute. Because both the operations define messages in the same namespace, and both return types contain an element ReturnValue
, you will get a conflict where the urn:ReturnValue
element is defined twice, once as an int and once as a string.
To demonstrate see the following, with the GetIntData
operation comment out, see the ReturnValue
element defined:
<!-- from the XSD http://localhost/Service1.svc?xsd=xsd0 -->
<xs:element name="SingleTypeResponseOf_String">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" ref="q1:ReturnValue"
xmlns:q1="http://tempuri.org/" />
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- from the XSD http://localhost/Service1.svc?xsd=xsd2 -->
<xs:schema elementFormDefault="qualified"
targetNamespace="http://tempuri.org/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://tempuri.org/">
<xs:element name="ReturnValue"
nillable="true"
type="xs:string" />
</xs:schema>
How can you allow the MessageContract
to rename the ReturnValue
property? I don't think you can with DataContractSerializer.
What's the solution? Well, I don't think there is a solution using [MessageBodyMember]
. If you use [DataMember]
it will work fine. You mentioned before that you're using net.tcp, so I assume your .NET to .NET. Do you need to exercise that level of control of the SOAP envelopes?
Normally I use DataContract
as much as possible, and only venture into MessageContract
when necessary - interfacing with older SOAP style platforms.
精彩评论