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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions