wcf: Incorrect generated client, two-dimensional array generated in place of list (WCF Connected Services)

Hacked WSDL: https://gist.github.com/Ravenheart/dc92c2023d3536798aca3e721f5a85ae

In relation to https://github.com/dotnet/wcf/issues/1269 I’ve had some help from the company that made the service and they created a special version that doesn’t have endless recursion possible. Now this new version is added successfully to a sample project BUT the actual generated code throws a runtime exception.

System.AggregateException: One or more errors occurred. (System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e is not assignable from System.String[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e.) ---> System.InvalidOperationException: System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e is not assignable from System.String[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e.
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at TestConsoleNETCore.Program.Main(String[] args) in D:\DEV\TestConsoleNETCore\src\TestConsoleNETCore\Program.cs:line 17
---> (Inner Exception #0) System.InvalidOperationException: System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e is not assignable from System.String[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e.
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult)<---

The error in question is related to complex types that may end up with properties like “public string[][] SomeProperty” and according to some googling around this is also a bug with .NET’s tooling (tested it with wsdl.exe, svcutil.exe and the new connected services util).

Generated client: https://gist.github.com/Ravenheart/b33e79eba6519088731804b11d8bb6bd

Usage:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace TestConsoleNETCore
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                PISServiceWS.PISWebServiceClient client = new PISServiceWS.PISWebServiceClient();

                //exception here
                var res = client.vldContractAsync(new PISServiceWS.vldContractIn()
                {
                    Item = "8610166253",
                    ItemElementName = PISServiceWS.ItemChoiceType9.egn,

                    issueDate = new DateTime(2016, 6, 6),
                    issueDateSpecified = true,

                    practiceNo = "2211131508",
                    specCode = "01",
                    uin = "0500000012",
                }).Result;

                Console.WriteLine(res.vldContractOut.vld);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 1
  • Comments: 29 (9 by maintainers)

Commits related to this issue

Most upvoted comments

@Ravenheart The generated code for the two dimensional array tr is not correct. In XmlArrayItemAttribute, we need either use typeof(string[]) or add NestingLevel=1. We’ll look at the root cause.

To work around this issue now, you can edit the generated code and make the change yourself as below. [System.Xml.Serialization.XmlArrayAttribute(Order=1)] [System.Xml.Serialization.XmlArrayItemAttribute(“td”, typeof(string), IsNullable=false)] public string[][] tr {}

Either use [System.Xml.Serialization.XmlArrayItemAttribute(“td”, typeof(string[]), IsNullable=false)] Or [System.Xml.Serialization.XmlArrayItemAttribute(“td”, typeof(string), IsNullable=false, NestingLevel=1)]

@eng-marani its been there since the early 2000s 😃

It’s a shame that this issue is closed but still not actually fixed 7 years after it was reported. 😞

Anyway, here’s a small C# program that will automatically fix auto generated reference files as explained by @huanwu in https://github.com/dotnet/wcf/issues/1274#issuecomment-228206943

using System.IO;
using System.Text.RegularExpressions;

// Automatically perform fix mentioned in https://github.com/dotnet/wcf/issues/1274#issuecomment-228206943

foreach (var filePath in args)
{
    FixXmlArrayItemAttributes(filePath);
}

static void FixXmlArrayItemAttributes(string filePath)
{
    var typeofRegex = new Regex(@"(.*)typeof\((.*?)\)(.*)");
    var lines = File.ReadAllLines(filePath);
    for (var i = 0; i < lines.Length; i++)
    {
        var line = lines[i];
        const string marker = "//     This code was generated by a tool and [XmlArrayItem] attributes were later fixed by another tool.";
        if (line.StartsWith(marker))
        {
            return;
        }
        if (line.StartsWith("//     This code was generated by a tool."))
        {
            lines[i] = marker;
        }
        if (line.Contains("[System.Xml.Serialization.XmlArrayItemAttribute"))
        {
            var propertyLine = GetPropertyLine(lines, i);
            if (propertyLine.Contains("[][]") && typeofRegex.IsMatch(line))
            {
                lines[i] = typeofRegex.Replace(line, "$1typeof($2[])$3");
            }
        }
    }
    File.WriteAllLines(filePath, lines);
}

static string GetPropertyLine(string[] lines, int index)
{
    while (true)
    {
        var line = lines[++index];
        if (!line.Contains("[System.Xml"))
            return line;
    }
}

The code is not pretty but it can surely spare you some time doing it manually if your reference files are huge.

Warning Run this fix on files under source control because it will replace contents in place.

It’s incredible, but this compile error is still there in 2020 !!!

I’m building a soap proxy for SalesForce web service, and the error is this (typeof need an array type):

        [System.Xml.Serialization.XmlArrayItemAttribute("columns", typeof(ListViewRecordColumn), IsNullable=false)]
        public ListViewRecordColumn[][] records;

must be

        [System.Xml.Serialization.XmlArrayItemAttribute("columns", typeof(ListViewRecordColumn[]), IsNullable=false)]
        public ListViewRecordColumn[][] records;

Do exist any workaround at compiler level except manually fixing it ?

Thank you for the feedback, but our team was unable to prioritize it at this time as they are working on problems with broader customer impact. This bug has existed in similar tools for a long time and there is a known work around.

Hi @ipjohnson We will hopefully look into this in the next sprint. thanks

Thank you for the feedback, but our team was unable to prioritize it at this time as they are working on problems with broader customer impact. This bug has existed in similar tools for a long time and there is a known work around.

@mlacouture @dasetser @HongGit @zhenlan @huanwu @hongdai @shmao

It’s shameful that this bug has existed for so long and the “known workaround” recommended by Microsoft is to manually edit an auto-generated file! I’m in disbelief that you’d recommend manually editing a generated file and expect that to be acceptable and maintainable.

Why can’t this be resolved correctly?

I guess it’s in xsd.exe as well, not just svcutil, ran xsd on AudDataTypes.xsd from the url https://www.vwe.nl/Developers, I ended up with

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.xmlmode.nl/interdata/aud")]
    public partial class audaconInfoIntervalTypeV1 {
        
        private intervalItem[][] intervallenField;
        
        /// <remarks/>
        [System.Xml.Serialization.XmlArrayItemAttribute("interval", typeof(intervalItem), IsNullable=false)]
        public intervalItem[][] intervallen {
            get {
                return this.intervallenField;
            }
            set {
                this.intervallenField = value;
            }
        }
    }

What I wanted was somthing like this, or at least something that works out of the box:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.xmlmode.nl/interdata/aud")]
    public partial class audaconInfoIntervalTypeV1 {

        private intervallen[] intervallenField;

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("intervallen")]
        public intervallen[] intervallen
        {
            get
            {
                return this.intervallenField;
            }
            set
            {
                this.intervallenField = value;
            }
        }
    }