прокси-класс svcutil WCF отправляет неверное действие SOAP по запросу - PullRequest
4 голосов
/ 25 июля 2011

Я использую 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;
    }
}

1 Ответ

0 голосов
/ 26 июля 2011

Единственный ответ, который я до сих пор придумал, - это создать дополнительный тип сообщения с идентичными частями, т. Е .:

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

Мне действительно не нравится это решение, так как я создал избыточное сообщение с точно такой же структурой. Похоже, что svcutil должен быть в состоянии генерировать разные действия SOAP для одного и того же типа сообщения. И, на самом деле, если ответ не является массивом, он прекрасно справляется с этим (я проверял операции, которые возвращают, скажем, bool, и они могут совместно использовать одно и то же сообщение запроса, и код генерирует правильно).

...