Skip to content

dotnet-svcutil generates invalid IL code if the web service method has an optional parameter #5862

@slepmog

Description

@slepmog

Describe the bug
When an ASMX web service has an optional parameter (one marked with System.ComponentModel.DefaultValueAttribute), dotnet-svcutil with "serializer": "XmlSerializer" generates for that service code that fails upon trying to call that method, with the following stack trace:

System.ServiceModel.CommunicationException: There was an error in serializing body of message FrobberPlopRequest: 'There was an error generating the XML document.'.  Please see InnerException for more details.
 ---> System.InvalidOperationException: There was an error generating the XML document.
 ---> System.InvalidProgramException: Invalid IL code in Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterMyApplicationSoap:Write40_FrobberPlop (object[]): IL_01bb: ceq       


   at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer14.Serialize(Object objectToSerialize, XmlSerializationWriter writer)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle)
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, Object returnValue, Object[] parameters) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs:line 390
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs:line 330
   --- End of inner exception stack trace ---
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs:line 363
   at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/OperationFormatter.cs:line 370
   at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/OperationFormatter.cs:line 773
   at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer) in  #/_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/BodyWriter.cs:line 132
   at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/Message.cs:line 1191
   at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/Message.cs:line 779
   at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/Message.cs:line 711
   at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/BufferedMessageWriter.cs:line 60
   at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessageAsync(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/TextMessageEncoder.cs:line 527
   at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/TextMessageEncoder.cs:line 475
   at System.ServiceModel.Channels.MessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/MessageEncoder.cs:line 120
   at System.ServiceModel.Channels.BufferedMessageContent.EnsureMessageEncoded() in /_/src/System.ServiceModel.Http/src/System/ServiceModel/Channels/MessageContent.cs:line 247
   at System.ServiceModel.Channels.BufferedMessageContent.TryComputeLength(Int64& length) in /_/src/System.ServiceModel.Http/src/System/ServiceModel/Channels/MessageContent.cs:line 268
   at System.Net.Http.HttpContent.GetComputedOrBufferLength()
   at System.Net.Http.Headers.HttpContentHeaders.get_ContentLength()
   at Xamarin.Android.Net.AndroidMessageHandler.SetupRequestBody(HttpURLConnection httpConnection, HttpRequestMessage request) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 1340
   at Xamarin.Android.Net.AndroidMessageHandler.SetupRequestInternal(HttpRequestMessage request, URLConnection conn) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 1134
   at Xamarin.Android.Net.AndroidMessageHandler.DoSendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 496
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at   ##System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.<SendRequestAsync>d__13[[System.ServiceModel.Channels.IRequestChannel, System.ServiceModel.Primitives, Version=8.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext() in /_/src/System.ServiceModel.Http/src/System/ServiceModel/Channels/HttpChannelFactory.cs:line 1056
   at System.ServiceModel.Channels.RequestChannel.RequestAsync(Message message, TimeSpan timeout) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/RequestChannel.cs:line 267
   at System.ServiceModel.Channels.RequestChannel.RequestAsyncInternal(Message message, TimeSpan timeout) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/RequestChannel.cs:line 238
   at System.Runtime.TaskHelpers.WaitForCompletionNoSpin[Message](Task`1 task) in /_/src/System.ServiceModel.Primitives/src/Internals/System/Runtime/TaskHelpers.cs:line 294
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/RequestChannel.cs:line 227
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/RequestChannelBinder.cs:line 107
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannel.cs:line 760
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannel.cs:line 717
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(MethodCall methodCall, ProxyOperationRuntime operation) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannelProxy.cs:line 390
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(MethodInfo targetMethod, Object[] args) in /_/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannelProxy.cs:line 144
   at generatedProxy_1.FrobberPlop(FrobberPlopRequest )
   at MyApplication.FrobberService.FrobberSoapClient.MyApplication.FrobberService.FrobberServiceSoap.FrobberPlop(FrobberPlopRequest request) in C:\Users\...\Reference.cs:line 2586
   at MyApplication.FrobberService.FrobberSoapClient.FrobberPlop(String Batch, Nullable`1 Zone, String Template, Byte NumberOfPlops, Int32 PersonID, String FrobberName, Boolean AllowCreate, Boolean AllowContinue) in C:\Users\...\Reference.cs:line 2600
   at MyApplication.MainActivity.StartFrobberPlop(String frobber_name, Byte number_of_plops) in C:\Users\...\MainActivity.cs:line 1391}	System.ServiceModel.CommunicationException

To Reproduce
Have an ASMX web service that exposes a similar method:

public DoesntMatterWhat FrobberPlop(
    string Batch,
    char? Zone,
    string Template,
    byte NumberOfPlops,
    int PersonID,
    string FrobberName,
    [System.ComponentModel.DefaultValue(true)] bool AllowCreate,
    [System.ComponentModel.DefaultValue(true)] bool AllowContinue
)

Generate a wrapper for it using dotnet-svcutil and the following ConnectedService.json:

{
  "ExtendedData": {
    "inputs": [
      "https://very.intranet.address.com/Services/FrobberService/FrobberService.asmx"
    ],
    "collectionTypes": [
      "System.Array",
      "System.Collections.Generic.Dictionary`2"
    ],
    "namespaceMappings": [
      "*, MyApplication.FrobberService"
    ],
    "sync": true,
    "targetFramework": "net9.0-android",
    "typeReuseMode": "All",
    "serializer": "XmlSerializer"
  }
}

Observe that that following classes and methods have been generated in Reference.cs:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "8.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="FrobberPlop", WrapperNamespace="http://very.intranet.address.com/ns/xml-services/frobber", IsWrapped=true)]
public partial class FrobberPlopRequest
{
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=0)]
    public string Batch;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=1)]
    [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
    public System.Nullable<char> Zone;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=2)]
    public string Template;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=3)]
    public byte NumberOfPlops;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=4)]
    public int PersonID;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=5)]
    public string FrobberName;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=6)]
    [System.ComponentModel.DefaultValueAttribute(true)]
    public bool AllowCreate;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=7)]
    [System.ComponentModel.DefaultValueAttribute(true)]
    public bool AllowContinue;
    
    public FrobberPlopRequest()
    {
    }
    
    public FrobberPlopRequest(string Batch, System.Nullable<char> Zone, string Template, byte NumberOfPlops, int PersonID, string FrobberName, bool AllowCreate, bool AllowContinue)
    {
        this.Batch = Batch;
        this.Zone = Zone;
        this.Template = Template;
        this.NumberOfPlops = NumberOfPlops;
        this.PersonID = PersonID;
        this.FrobberName = FrobberName;
        this.AllowCreate = AllowCreate;
        this.AllowContinue = AllowContinue;
    }
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "8.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", ConfigurationName="MyApplication.FrobberService.FrobberServiceSoap")]
public interface FrobberServiceSoap
{
    // CODEGEN: Parameter 'Zone' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'Microsoft.Xml.Serialization.XmlElementAttribute'.
    [System.ServiceModel.OperationContractAttribute(Action="http://very.intranet.address.com/ns/xml-services/frobber/FrobberPlop", ReplyAction="*")]
    [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(SomeNestedResult))]
    MyApplication.FrobberService.FrobberPlopResponse FrobberPlop(MyApplication.FrobberService.FrobberPlopRequest request);
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "8.0.0")]
public partial class FrobberSoapClient : System.ServiceModel.ClientBase<MyApplication.FrobberService.FrobberServiceSoap>, MyApplication.FrobberService.FrobberServiceSoap
{
    public MyApplication.FrobberService.DoesntMatterWhat FrobberPlop(string Batch, System.Nullable<char> Zone, string Template, byte NumberOfPlops, int PersonID, string FrobberName, bool AllowCreate, bool AllowContinue)
    {
        MyApplication.FrobberService.FrobberPlopRequest inValue = new MyApplication.FrobberService.FrobberPlopRequest();
        inValue.Batch = Batch;
        inValue.Zone = Zone;
        inValue.Template = Template;
        inValue.NumberOfPlops = NumberOfPlops;
        inValue.PersonID = PersonID;
        inValue.FrobberName = FrobberName;
        inValue.AllowCreate = AllowCreate;
        inValue.AllowContinue = AllowContinue;
        MyApplication.FrobberService.FrobberPlopResponse retVal = ((MyApplication.FrobberService.FrobberServiceSoap)(this)).FrobberPlop(inValue);
        return retVal.DoesntMatterWhat;
    }
}

Attempt to consume the service from the client code:

using (var c = new FrobberService.FrobberServiceSoapClient(FrobberServiceSoapClient.EndpointConfiguration.FrobberServiceSoap12))
{
    var result = c.FrobberFlop(batch, zone, template, plops, person_id, "Frobber12", true, true);
}

Observe the exception quoted above with

System.InvalidProgramException: Invalid IL code in Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterMyApplicationSoap:Write40_FrobberPlop (object[]): IL_01bb: ceq

Workaround
You can comment out the DefaultValueAttribute from the generated class, in which case everything will start working:

public partial class FrobberPlopRequest
{
    ...
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=6)]
    //[System.ComponentModel.DefaultValueAttribute(true)]  // Commented out - crashes at runtime
    public bool AllowCreate;
    
    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://very.intranet.address.com/ns/xml-services/frobber", Order=7)]
    //[System.ComponentModel.DefaultValueAttribute(true)]  // Commented out - crashes at runtime
    public bool AllowContinue;
}

Expected behaviour
The generated code does not crash at runtime, whether or not it has to process the DefaultValueAttribute.

Additional context
I am seeing this in an Android application that uses the new .NET Android template and depends on an ASMX webservice. I don't know if it manifests in other types of projects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions