4

Proxy generated with dotnet-svcutil 2.0.1 (dotnet-svcutil --sync --outputDir . http://XXX/?WSDL) and System.ServiceModel.* 4.7.0, calling code and?WSDL below. The proxy simply fails to deserialize the valid response and just returns null. Have tried both .NET Core 3.0 and 3.1 on Windows 10 and macOS Catalina, same null result. Fiddler request and response attached along with?WSDL from the server (server beyond my control).

With the proxy I am using @shmao's set_mode workaround (https://github.com/dotnet/wcf/issues/2219) to avoid the 'JScript/CSharp scripts is not supported' exception. Further, I have to remove the Namespace="" attributes to get even the request part working. I have added EventListeners and dumped everything at Verbose from all event sources, no warnings/errors, just the null.

I have also tried the Channel-based MessageContract/DataContract approach, ultimately same null result (!), I am unable to leverage any .NET Core WCF-based code to deserialize the given response.

Any solution or even partial solution to deserializing given response using .NET Core 3.1 WCF will be considered, ideally with dotnet-svcutil. Uncovering a warning/error or even getting access to the response string manually would still be an improvement over a non-WCF string/HttpRequest-based approach.

      WSWebServiceSoapPortClient proxy;
      try {
        proxy = new WSWebServiceSoapPortClient(new BasicHttpBinding(),
          new EndpointAddress("http://XXX"));

        await proxy.OpenAsync();
      } catch (Exception e) {
        Console.WriteLine(e.Message);
        return;
      }

      if (proxy.State == System.ServiceModel.CommunicationState.Faulted) {
        System.Console.WriteLine("Unable to connect to the proxy.");
        return;
      }

      var one = new WSUserLoginRequest1(new WSUserLoginRequest() {
        userName = "XXX",
        userPassword = "XXX",
      });
      WSUserLoginResponse1 wsUserLoginResponse = null;

      try {
        wsUserLoginResponse = await proxy.WSUserLoginAsync(one);   // returns null
      } catch (Exception e) {
        Console.WriteLine(e.ToString());
        return;
      }

Relevant WSDL from server

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
  xmlns:s="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
  xmlns:tns="WSWebService" name="WSWebService" targetNamespace="WSWebService">
  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="WSWebService">

      ...

      <s:complexType name="WSUserLoginRequest">
        <s:sequence>
          <s:element name="userName" type="s:string" />
          <s:element name="userPassword" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="WSUserLoginResponse">
        <s:sequence>
          <s:element name="userToken" type="s:string" />
          <s:element name="wsdlVersion" type="s:string" minOccurs="1" maxOccurs="1" default="2.0.0.0" />
          <s:element name="result" type="s:int" />
          <s:element name="resultString" type="s:string" />
        </s:sequence>
      </s:complexType>

      ...

      <wsdl:message name="WSUserLoginSoapIn">
        <wsdl:part name="parameters" type="tns:WSUserLoginRequest" />
      </wsdl:message>
      <wsdl:message name="WSUserLoginSoapOut">
        <wsdl:part name="parameters" type="tns:WSUserLoginResponse" />
      </wsdl:message>

      ...

      <wsdl:operation name="WSUserLogin">
        <wsdl:documentation>Authenticate user using provided username and password.</wsdl:documentation>
        <wsdl:input message="tns:WSUserLoginSoapIn" />
        <wsdl:output message="tns:WSUserLoginSoapOut" />
      </wsdl:operation>

request response

(EDIT)

The class definition for WSUserLoginResponse1 as generated by dotnet-svcutil 2.0.1:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.ServiceModel.MessageContractAttribute(WrapperName = "WSUserLoginResponse", WrapperNamespace = "WSWebService", IsWrapped = true)]
public partial class WSUserLoginResponse1 {

  [System.ServiceModel.MessageBodyMemberAttribute(Namespace = "", Order = 0)]
  public WSUserLoginResponse parameters;

  public WSUserLoginResponse1() {
  }

  public WSUserLoginResponse1(WSUserLoginResponse parameters) {
    this.parameters = parameters;
  }
}

(EDIT 2) WSUserLoginResponse from dotnet-svcutil as suggested.

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "WSWebService")]
public partial class WSUserLoginResponse {

  private string userTokenField;

  private string wsdlVersionField;

  private int resultField;

  private string resultStringField;

  public WSUserLoginResponse() {
    this.wsdlVersionField = "2.0.0.0";
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]
  public string userToken {
    get {
      return this.userTokenField;
    }
    set {
      this.userTokenField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 1)]
  public string wsdlVersion {
    get {
      return this.wsdlVersionField;
    }
    set {
      this.wsdlVersionField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 2)]
  public int result {
    get {
      return this.resultField;
    }
    set {
      this.resultField = value;
    }
  }

  /// <remarks/>
  [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 3)]
  public string resultString {
    get {
      return this.resultStringField;
    }
    set {
      this.resultStringField = value;
    }
  }
}

(EDIT 3) WSUserLoginResponse from wsdl.exe as suggested. There is no WSUserLoginResponse1

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1087.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="WSWebService")]
    public partial class WSUserLoginResponse : object, System.ComponentModel.INotifyPropertyChanged {

        private string userTokenField;

        private string wsdlVersionField;

        private int resultField;

        private string resultStringField;

        public WSUserLoginResponse() {
            this.wsdlVersionField = "2.0.0.0";
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=0)]
        public string userToken {
            get {
                return this.userTokenField;
            }
            set {
                this.userTokenField = value;
                this.RaisePropertyChanged("userToken");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=1)]
        public string wsdlVersion {
            get {
                return this.wsdlVersionField;
            }
            set {
                this.wsdlVersionField = value;
                this.RaisePropertyChanged("wsdlVersion");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=2)]
        public int result {
            get {
                return this.resultField;
            }
            set {
                this.resultField = value;
                this.RaisePropertyChanged("result");
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, Order=3)]
        public string resultString {
            get {
                return this.resultStringField;
            }
            set {
                this.resultStringField = value;
                this.RaisePropertyChanged("resultString");
            }
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null)) {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }

(EDIT 4) Standalone wsdl.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
  xmlns:s="http://www.w3.org/2001/XMLSchema" 
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
  xmlns:tns="WSWebService" name="WSWebService" targetNamespace="WSWebService">
  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="WSWebService">
      <s:complexType name="WSUserLoginRequest">
        <s:sequence>
          <s:element name="userName" type="s:string" />
          <s:element name="userPassword" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="WSUserLoginResponse">
        <s:sequence>
          <s:element name="userToken" type="s:string" />
          <s:element name="wsdlVersion" type="s:string" minOccurs="1" maxOccurs="1" default="2.0.0.0" />
          <s:element name="result" type="s:int" />
          <s:element name="resultString" type="s:string" />
        </s:sequence>
      </s:complexType>
    </schema>
  </wsdl:types>
  <wsdl:message name="WSUserLoginSoapIn">
    <wsdl:part name="parameters" type="tns:WSUserLoginRequest" />
  </wsdl:message>
  <wsdl:message name="WSUserLoginSoapOut">
    <wsdl:part name="parameters" type="tns:WSUserLoginResponse" />
  </wsdl:message>
  <wsdl:portType name="WSWebServiceSoapPort">
    <wsdl:operation name="WSUserLogin">
      <wsdl:documentation>Documentation</wsdl:documentation>
      <wsdl:input message="tns:WSUserLoginSoapIn" />
      <wsdl:output message="tns:WSUserLoginSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="WSWebServiceSoapBinding" type="tns:WSWebServiceSoapPort">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="WSUserLogin">
      <soap:operation soapAction="WSUserLogin" style="rpc" />
      <wsdl:input>
        <soap:body use="literal" namespace="WSWebService" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" namespace="WSWebService" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="WSWebService">
    <wsdl:port name="WSWebServiceSoapPort" binding="tns:WSWebServiceSoapBinding">
      <soap:address location="https://x.x.x.x:x" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
bunt
  • 302
  • 2
  • 13
  • 25
  • can you add the generated c# definition of `WSUserLoginResponse1`? also, why it ends with `1` - are there both `WSUserLoginResponse` and ``WSUserLoginResponse1`? – OfirD Feb 27 '20 at 09:32
  • @HeyJude, WSUserLoginResponse1 is defined in the Reference.cs generated by dotnet-svcutil, WSUserLoginAsync return type is Task. – bunt Feb 28 '20 at 00:22
  • You didn't answered what I asked in my comment, so I guess I should explain: I want you to add in your question the generated code of `WSUserLoginResponse1`, as it may not have generated properly. It happens sometimes. The fact that it ends with `1` is a bit suspicious. – OfirD Feb 28 '20 at 05:48
  • Perhaps you can use the Visual Studio wizard to generate the requisite code and see if the resulting output is any different than the `dotnet-svcutil` output? https://learn.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide – rbonestell Feb 28 '20 at 14:58
  • @bunt, I see you added `WSUserLoginResponse1` definition, that's great. Now, since this class contains a field named `parameters`, whose type is `WSUserLoginResponse`, it worth also adding *that* generated c# definition. – OfirD Feb 29 '20 at 17:00
  • @bunt, another option, similiar to what @rbonestell suggested: try to generate the c# code by using `wsdl.exe`. It should be built in if you'd use [VS cmd](https://learn.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs), so just open (as administrator) this cmd, then type `wsdl.exe path/to/wsdl/file` or `wsdl.exe url/of/wsdl/file`. – OfirD Mar 01 '20 at 09:15
  • Everything requested added, along with valid .wsdl w/ just the method in question. Run this with .net framework and wsdl.exe and the supplied response deserializes, run this w/ dotnet-svcutil and .net core and the supplied response returns null. Will award bounty for any even half-baked workaround using .net core wcf (vs. e.g. raw string manipulation w/ httprequest). – bunt Mar 01 '20 at 12:17
  • @bunt, do you really prefer working with a raw string response (in which case, the generated c# is kind of unnecessary) instead of going with the working code generated by `wsdl.exe`? Because if that's what you want, you can just fire an [http request](https://stackoverflow.com/a/4791932/3002584). – OfirD Mar 01 '20 at 17:50

1 Answers1

3

I used your wsdl file to generate the c# code using dotnet-svcutil 2.0.1 as you did.

I mocked a SOAP endpoint using SoapUI mocking service, fired it, and then ran your code on my machine (connecting to the mocked endpoint on my localhost).

I got an exception early on calling proxy.OpenAsync. The exception message I got was:

The top XML element 'parameters' from namespace '' references distinct types WSUserLoginRequest and WSUserLoginResponse. Use XML attributes to specify another XML name or namespace for the element or types.

So I went to WSUserLoginRequest1 inside the generated code and added a value to the Namespace of parameters, as explained here:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1")]
[System.ServiceModel.MessageContractAttribute(WrapperName="WSUserLogin", WrapperNamespace="WSWebService", IsWrapped=true)]
public partial class WSUserLoginRequest1
{
    // replaced Namespace empty string value with my endpoint url
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://localhost:8181/WSUserLogin", Order=0)]
    public WSUserLoginRequest parameters;

    public WSUserLoginRequest1()
    {
    }

    // ...
}

After that change, it just worked, and I got back a response when calling proxy.WSUserLoginAsync.

I wish I knew how to add that Namespace value by passing values to the /namespace option of dotnet-svcutil, but following these SO posts (1, 2, though they relate to svcutil, not dotnet-svcutil) only added namespaces to other places in the generated file.

Additionaly, as you noted, using wsdl.exe doesn't generate the unneccesary WSUserLoginResponse1, while svcutil-dotnet does. This seems to be a known problem, see e.g. here: svcutil generated unneccesary wrapper classes.

I hope it would be of any value for your problem.

OfirD
  • 9,442
  • 5
  • 47
  • 90
  • Bingo nicely done @HeyJude, it works for me to. Where I wrote "Further, I have to remove the Namespace="" attributes to get even the request part working", don't do this, instead use a unique string. – bunt Mar 02 '20 at 03:27