Я использую C # / WCF (.NET 3.5 SP1) и пытаюсь получить доступ к службе SOAP, размещенной через Apache / PHP. Сначала был создан WSDL, а затем была создана служба, соответствующая WSDL. Я пытаюсь использовать svcutil для создания прокси-классов WCF для доступа к этому сервису.
Я обнаружил явную ошибку генерации кода в svcutil. Похоже, это происходит потому, что есть две отдельные операции SOAP, которые совместно используют общее входное сообщение (используя разные действия SOAP) и выводят два разных типа массива. Когда svcutil генерирует код, обе операции генерируются с использованием одного и того же класса запроса и разных классов ответа, а OperationContractAttribute используется для установки действия для операций. К сожалению, 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;
}
}