views:

490

answers:

1

Could someone please shed some light on this behaviour? It looks like Delphi SOAP sets the function result as the last argument, but WSDL.exe reads the first argument to be the function result.

I have the following method in a Delphi SOAP service, where the result string is used for basic error handling:

function LoadCustomer(CustomerID: Double; out CustomerName: String): String;

The generated WSDL looks like this:

<message name="LoadCustomer2Request">
  <part name="CustomerID" type="xs:double"/>
</message>
<message name="LoadCustomer2Response">
  <part name="CustomerName" type="xs:string"/>
  <part name="return" type="xs:string"/>
</message>

For some reason, WSDL.exe generates the below C# code which swaps the CustomerName and 'Result' strings:

public string LoadCustomer(double CustomerID, out string @return) {
        WindowsFormsApplication1.ServiceReference1.LoadCustomerRequest inValue = new WindowsFormsApplication1.ServiceReference1.LoadCustomerRequest();
        inValue.CustomerID = CustomerID;
        WindowsFormsApplication1.ServiceReference1.LoadCustomerResponse retVal = ((WindowsFormsApplication1.ServiceReference1.ISKiWebInterface)(this)).LoadCustomer(inValue);
        @return = retVal.@return;
        return retVal.CustomerName;
    }
+10  A: 

Hello,

When SOAP was predominantly rpc-oriented issues like these came up often. There's no specific order to determine which part is the function's (operation's) result. Delphi's own importer used to [and probably still does??] identify the 'result' by the part's name. And you could (can??) specify a comma-delimited list of names to use. If none of the parts match the names, then if there's a single out, it's the result.

The SOAP spec. did eventually include additions to solve this issue. The relevant one in your case is the 'parameterOrder' attribute (there's also rpc:result for the actual SOAP data). However, you hardly see WSDLs that use the attribute. But, I believe that WSDL.EXE does pay attention to that attribute. You can find out more about parameterOrder here:

http://www.w3.org/TR/wsdl#_parameter

What I would suggest you do is to save the WSDL generated by Delphi to a file; and update the later to include the parameterOrder attribute(*). For example, in the case you provided, you'll want to find the portType that maps to the interface and update the operation as follows:

  <portType name="InterfaceName">
    <operation name="LoadCustomer" parameterOrder="CustomerId, CustomerName">
      <input message="tns:LoadCustomer2Request"/>
      <output message="tns:LoadCustomer2Response"/>
    </operation>
  </portType>

Then, importing that updated WSDL with WSDL.EXE should give you something along the lines of:

  public string LoadCustomer(out string CustomerName, double CustomerID) {
    object[] results = this.Invoke("LoadCustomer", new object[] {
                CustomerID});
    CustomerName = ((string)(results[1]));
    return ((string)(results[0]));
  }

You should also see the following attribute above the method to confirm that 'return' is the indeed result:

 [return: System.Xml.Serialization.SoapElementAttribute("return")]

I would suggest opening a QC requesting that the parameterOrder be generated by Delphi's WSDL logic.

Cheers,

Bruneau

PS: (*) It's also easy to update the WSDL generation logic to emit parameterOrder. It's been a loooong time since I've been in that code but it's fairly straight-forward (if I remember correctly:)

BruneauB