我正在使用C#/ WCF(.NET 3.5 SP1)并尝试访问通过Apache / PHP托管的SOAP服务。首先生成WSDL,然后构建服务以符合WSDL。我正在尝试使用svcutil生成WCF代理类来访问此服务。
我在svcutil中发现了明显的代码生成错误。这似乎是因为有两个单独的SOAP操作共享一个公共输入消息(使用不同的SOAP操作)并输出两个不同的数组类型。当svcutil生成代码时,两个操作都使用相同的请求类和不同的响应类生成,而OperationContractAttribute用于设置操作的Action。不幸的是,在请求类上设置了MessageContractAttribute,它似乎重写了这一点,导致两个服务调用都包含相同的SOAP操作。
我能够通过手动修改生成的代码来修正此问题,以便使用不同的MessageContractAttribute为第二个操作创建单独的类,并更新所有其他引用以使用此类。但我真的不满意这个解决方案,我正在寻找一种方法来调整WSDL,以便svcutil可以更好地解释它,或者将命令行参数更改为svcutil,以便它可以生成正确的代码。
另外注意,DataContractSerializer无法导入此WSDL,它必须使用XmlSerializer。因此,如果有人可以建议修改WSDL以便DataContractSerializer可以运行的方法,我会很乐意尝试 - 我可以完全控制WSDL。
我能够创建一个非常简单的WSDL来说明这一点:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.example.org/ArrayTest/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="ArrayTest"
targetNamespace="http://www.example.org/ArrayTest/">
<wsdl:types>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/ArrayTest/"
>
<xsd:complexType name="baseType1">
<xsd:sequence>
<xsd:element name="string1" type="xsd:string" />
<xsd:element name="string2" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="listType1">
<xsd:sequence>
<xsd:element name="baseType" type="tns:baseType1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="baseType2">
<xsd:sequence>
<xsd:element name="string1" type="xsd:string" />
<xsd:element name="string2" type="xsd:string" />
<xsd:element name="bool1" type="xsd:boolean" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="listType2">
<xsd:sequence>
<xsd:element name="baseType" type="tns:baseType2" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="NewOperationRequest">
<wsdl:part name="NewOperationRequest" type="xsd:string"/>
</wsdl:message>
<wsdl:message name="Array1OperationResponse">
<wsdl:part name="list1" type="tns:listType1"/>
</wsdl:message>
<wsdl:message name="Array2OperationResponse">
<wsdl:part name="list2" type="tns:listType2" />
</wsdl:message>
<wsdl:portType name="ArrayTest">
<wsdl:operation name="Array1Operation">
<wsdl:input message="tns:NewOperationRequest"/>
<wsdl:output message="tns:Array1OperationResponse"/>
</wsdl:operation>
<wsdl:operation name="Array2Operation">
<wsdl:input message="tns:NewOperationRequest"/>
<wsdl:output message="tns:Array2OperationResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ArrayTestSOAP" type="tns:ArrayTest">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Array1Operation">
<soap:operation
soapAction="http://www.example.org/ArrayTest/Array1Operation" />
<wsdl:input>
<soap:body use="literal"
namespace="http://www.example.org/ArrayTest/" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal"
namespace="http://www.example.org/ArrayTest/" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Array2Operation">
<soap:operation
soapAction="http://www.example.org/ArrayTest/Array2Operation" />
<wsdl:input>
<soap:body use="literal"
namespace="http://www.example.org/ArrayTest/" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal"
namespace="http://www.example.org/ArrayTest/" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ArrayTest">
<wsdl:port binding="tns:ArrayTestSOAP" name="ArrayTestSOAP">
<soap:address location="http://www.example.org/"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
这是传递给svcutil的命令:
svcutil /noconfig /o:arrayTest.cs ArrayTest.wsdl
以下是生成的代码:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.4959
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://www.example.org/ArrayTest/", ConfigurationName="ArrayTest")]
public interface ArrayTest
{
// CODEGEN: Parameter 'list1' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlArrayAttribute'.
[System.ServiceModel.OperationContractAttribute(Action="http://www.example.org/ArrayTest/Array1Operation", ReplyAction="*")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
[return: System.ServiceModel.MessageParameterAttribute(Name="list1")]
Array1OperationResponse Array1Operation(Array1OperationRequest request);
// CODEGEN: Parameter 'list2' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlArrayAttribute'.
[System.ServiceModel.OperationContractAttribute(Action="http://www.example.org/ArrayTest/Array2Operation", ReplyAction="*")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
[return: System.ServiceModel.MessageParameterAttribute(Name="list2")]
Array2OperationResponse Array2Operation(Array1OperationRequest request);
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.example.org/ArrayTest/")]
public partial class baseType1
{
private string string1Field;
private string string2Field;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
public string string1
{
get
{
return this.string1Field;
}
set
{
this.string1Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
public string string2
{
get
{
return this.string2Field;
}
set
{
this.string2Field = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.example.org/ArrayTest/")]
public partial class baseType2
{
private string string1Field;
private string string2Field;
private bool bool1Field;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
public string string1
{
get
{
return this.string1Field;
}
set
{
this.string1Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
public string string2
{
get
{
return this.string2Field;
}
set
{
this.string2Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
public bool bool1
{
get
{
return this.bool1Field;
}
set
{
this.bool1Field = value;
}
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Array1Operation", WrapperNamespace="http://www.example.org/ArrayTest/", IsWrapped=true)]
public partial class Array1OperationRequest
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
public string NewOperationRequest;
public Array1OperationRequest()
{
}
public Array1OperationRequest(string NewOperationRequest)
{
this.NewOperationRequest = NewOperationRequest;
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Array1OperationResponse", WrapperNamespace="http://www.example.org/ArrayTest/", IsWrapped=true)]
public partial class Array1OperationResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
[System.Xml.Serialization.XmlArrayAttribute()]
[System.Xml.Serialization.XmlArrayItemAttribute("baseType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public baseType1[] list1;
public Array1OperationResponse()
{
}
public Array1OperationResponse(baseType1[] list1)
{
this.list1 = list1;
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Array2OperationResponse", WrapperNamespace="http://www.example.org/ArrayTest/", IsWrapped=true)]
public partial class Array2OperationResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
[System.Xml.Serialization.XmlArrayAttribute()]
[System.Xml.Serialization.XmlArrayItemAttribute("baseType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public baseType2[] list2;
public Array2OperationResponse()
{
}
public Array2OperationResponse(baseType2[] list2)
{
this.list2 = list2;
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface ArrayTestChannel : ArrayTest, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class ArrayTestClient : System.ServiceModel.ClientBase<ArrayTest>, ArrayTest
{
public ArrayTestClient()
{
}
public ArrayTestClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public ArrayTestClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public ArrayTestClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public ArrayTestClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
Array1OperationResponse ArrayTest.Array1Operation(Array1OperationRequest request)
{
return base.Channel.Array1Operation(request);
}
public baseType1[] Array1Operation(string NewOperationRequest)
{
Array1OperationRequest inValue = new Array1OperationRequest();
inValue.NewOperationRequest = NewOperationRequest;
Array1OperationResponse retVal = ((ArrayTest)(this)).Array1Operation(inValue);
return retVal.list1;
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
Array2OperationResponse ArrayTest.Array2Operation(Array1OperationRequest request)
{
return base.Channel.Array2Operation(request);
}
public baseType2[] Array2Operation(string NewOperationRequest)
{
Array1OperationRequest inValue = new Array1OperationRequest();
inValue.NewOperationRequest = NewOperationRequest;
Array2OperationResponse retVal = ((ArrayTest)(this)).Array2Operation(inValue);
return retVal.list2;
}
}
答案 0 :(得分:0)
到目前为止,我提出的唯一答案是创建一个具有相同部分的附加消息类型,即:
<wsdl:message name="Array2OperationRequest">
<wsdl:part name="NewOperationRequest" type="xsd:string"/>
</wsdl:message>
我真的不喜欢这个解决方案,因为我创建了一个具有完全相同结构的冗余消息。似乎svcutil 应该能够为相同的消息类型生成不同的SOAP动作。而且,实际上,如果响应不是一个数组,它就可以做到这一点(我已经测试了返回的事件,比如一个bool,他们可以共享相同的请求消息并且代码生成正确)。