dotnet / wcf

This repo contains the client-oriented WCF libraries that enable applications built on .NET Core to communicate with WCF services.
MIT License
1.7k stars 556 forks source link

dotnet-svcutil cannot handle recursively nested XSD structures #5404

Open schneoka opened 7 months ago

schneoka commented 7 months ago

Here is an excerpt from our WSDL definition. The structure oel_CmisExtensionTypeList is recursively nested:

<xsd:complexType name="oel_CmisExtensionTypeList">
 <xsd:sequence>
  <xsd:element name="item" minOccurs="0" maxOccurs="unbounded" nillable="true" type="s0:oel_CmisExtensionType"/>
 </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="oel_CmisExtensionType">
 <xsd:sequence>
   <xsd:element name="key" minOccurs="0" maxOccurs="1" type="xsd:string"> </xsd:element>
   <xsd:element name="value" minOccurs="0" maxOccurs="1" type="xsd:string"> </xsd:element>
   <xsd:element name="any" minOccurs="0" maxOccurs="1" type="s0:oel_CmisExtensionTypeList"> </xsd:element>
 </xsd:sequence>
</xsd:complexType>

<xsd:element name="ObjectService__performTaskActionRequest">
 <xsd:complexType>
  <xsd:sequence>
   <xsd:element name="username" minOccurs="1" maxOccurs="1" type="xsd:string"> </xsd:element>
   <xsd:element name="extension" minOccurs="0" maxOccurs="1" type="s0:oel_CmisExtensionTypeList"> </xsd:element>
  </xsd:sequence>
 </xsd:complexType>
</xsd:element>

To generate the WCF class this command was used: dotnet-svcutil http://localhost:17089/wsdl --namespace "*, COI.SOAPConnector" --internal --sync --reference "c:\Program Files\dotnet\sdk\8.0.100\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Collections.dll" --noStdLib --outputFile BflowWSDL.cs --targetFramework "net8.0"

The generated class (I also tried dotnet-svcutil 2.1.0, it does not work either) looks like this:

  [System.Diagnostics.DebuggerStepThroughAttribute()]
   [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
   [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
   [System.ServiceModel.MessageContractAttribute(WrapperName="ObjectService__performTaskActionRequest", WrapperNamespace="http://www.coi.de/bflowsoap", IsWrapped=true)]
   internal partial class ObjectService__performTaskActionRequest
   {
       [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://www.coi.de/bflowsoap", Order=0)]
       public string username;

       [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://www.coi.de/bflowsoap", Order=3)]
       [System.Xml.Serialization.XmlArrayItemAttribute("item")]
       public COI.SOAPConnector.COI.SOAPConnector.oel_CmisExtensionType[] extension;

The definition of extension contains two errors: Firstly, the namespace COI.SOAPConnector appears twice: COI.SOAPConnector.COI.SOAPConnector.oel_CmisExtensionType[]
and secondly, the XSD definition contains a reference to oel_CmisExtensionTypeList, not to oel_CmisExtensionType

The svcutil used with .NET6 has generated the correct code here. Even if the duplicate namespace is corrected manually, the transfer of values in extension.any no longer works.

imcarolwang commented 5 months ago

Update:

  1. the double namespace issue doesn't repro on my side
  2. svcutil.exe behaves the same as dotnet-svcuitl.exe
  3. As reproduced base on the given information, oel_CmisExtensionTypeList type contains only one property, so the tool extracted that single member type (oel_CmisExtensionType[]) to the container type oel_CmisExtensionType, it doesn't seem to affect service op functionality if invoke by passing the required operation parameter. If type oel_CmisExtensionTypeList contains more than 1 property, type oel_CmisExtensionTypeList will be generated separately and be referenced as defined by the container type.
imcarolwang commented 5 months ago

@schneoka , since I was unable to reproduce the first "double namespace" problem, the example serivce/wsdl I created to reproduce the issue may be missing some key information compared to yours. Therefore, the conclusion mentioned in my previous comments for the second issue may not apply to you. Can you provide a fully constructed simplified wsdl file that reproduces the issue?

schneoka commented 5 months ago

@imcarolwang, i used this command to create the file: dotnet-svcutil http://localhost:17089/wsdl --namespace "*, COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector" --internal --sync --reference "c:\Program Files\dotnet\sdk\8.0.100\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Collections.dll" --noStdLib --outputDir .\CmisClient\Binding\SOAPConnector --outputFile BflowWSDL.cs --targetFramework "net8.0"

  1. For example, in the created file, line 5414:

public COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisExtensionType[] extension;

  1. Example with svcutil:
    internal partial class ObjectService__performTaskActionRequest
    {
    ...
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://www.coi.de/bflowsoap", Order=3)]
    public COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisExtensionTypeList extension;
    ..     
    public ObjectService__performTaskActionRequest(string username, string actionId, COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisPropertiesType properties, COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisExtensionTypeList extension)

    Example with dotnet-svcutil:

    internal partial class ObjectService__performTaskActionRequest
    {
    ...
    [System.Xml.Serialization.XmlArrayItemAttribute("item")]
    public COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisExtensionType[] extension;
    ... 
    public ObjectService__performTaskActionRequest(string username, string actionId, COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisPropertiesType properties, COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.COI.BusinessFlow.API.CmisClient.Binding.SOAPConnector.oel_CmisExtensionType[] extension)

    It was a lot of work to change the access to member extension from list to array type, but it still doesn't work. The extension parameter provides no data.

The WSDL WSDL.xml This is the created file BflowWSDL.cs

imcarolwang commented 5 months ago

@schneoka thank you for the information! It's very helpful, and I'm able to reproduce both of the issues described.

I found out that it was "xsd:integer" that was causing the problem, if update the given WSDL file by replacing the two occurrences of "xsd:integer" with "xsd:int", dotnet-svcutil will be able to generate the code as expected.

I'm wondering how you defined your service that its WSDL would contain "xsd:integer". Can you share the corresponding service contract definition? Thanks!

schneoka commented 5 months ago

@imcarolwang Isn't that the definition in https://www.w3.org/2001/XMLSchema?

<xs:simpleType name="integer" id="integer">
<xs:annotation>
<xs:documentation source="http://www.w3.org/TR/xmlschema-2/#integer"/>
</xs:annotation>
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="0" fixed="true" id="integer.fractionDigits"/>
<xs:pattern value="[\-+]?[0-9]+"/>
</xs:restriction>
</xs:simpleType>
imcarolwang commented 5 months ago

Hi @schneoka, I have a proposed fix to address this issue and I tried to run the private "fixed" build against the WSDL.xml you provided, here is the cs file that was generated: BflowWSDL2.txt, would you mind try it out in your project and see if it works?

schneoka commented 5 months ago

@imcarolwang The new cs file works in our project, thank you very much.

schneoka commented 1 month ago

Hi @imcarolwang, we have a change in our WSDL. Do you know when a new release will be available that contains the fix. Or is there a workaround for the problem.

mconnew commented 3 weeks ago

@schneoka, you can build and pack the product locally. Clone the repo, then run:

build -restore
build -build
build -pack

On high core count machines the build step sometimes fails due to an issue with parallel builds, if that happens, run it again. This will get you a local build of the nuget package which should unblock you.