svcutil WCF代理类根据请求提交不正确的SOAP操作

时间:2011-07-25 19:20:46

标签: wcf xmlserializer svcutil.exe

我正在使用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;
    }
}

1 个答案:

答案 0 :(得分:0)

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

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

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