WCF代理类在请求时提交不正确的SOAP操作



我使用的是c#/WCF。. NET 3.5 SP1),并试图访问通过Apache/PHP托管的SOAP服务。首先生成WSDL,然后构建服务以符合WSDL。我正在尝试使用svcutil来生成WCF代理类来访问此服务。

我在svcutil中发现了一个明显的代码生成错误。之所以会出现这种情况,是因为有两个独立的SOAP操作共享一个公共输入消息(使用不同的SOAP action),并输出两个不同的数组类型。当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;
    }
}

到目前为止,我想到的唯一答案是创建一个具有相同部分的额外消息类型,即:

<wsdl:message name="Array2OperationRequest">
    <wsdl:part name="NewOperationRequest" type="xsd:string"/>
</wsdl:message>

我真的不喜欢这个解决方案,因为我创建了一个具有完全相同结构的冗余消息。看起来svcutil 应该能够为相同的消息类型生成不同的SOAP action。而且,事实上,如果响应不是一个数组,它也能很好地做到这一点(我已经测试了返回bool的操作,它们可以共享相同的请求消息,并且代码正确生成)。

最新更新