Reputation: 2120
There is a specific WSDL for which the ServiceContractGenerator keeps on generating message contracts (request/response wrapper objects), which I do not want (I want straight parameters). Other WSDL's work fine.
When I use Visual Studio to create a WCF client ("Add Service Reference") and I click on "Advanced...", the checkbox which says "Always generate message contracts" does properly control whether the message contract objects are generated.
However, when I use the ServiceContractGenerator class to generate a WCF client programmatically, it keeps generating message contracts. I tried setting the ServiceContractGenerator's Options to ServiceContractGenerationOptions.None, but the result is the same.
Here is the code that I use:
MetadataSet metadataSet = new MetadataSet();
metadataSet.MetadataSections.Add(MetadataSection.CreateFromServiceDescription(System.Web.Services.Description.ServiceDescription.Read(wsdlStream)));
WsdlImporter importer = new WsdlImporter(metadataSet);
if (serviceDescription != null)
importer.WsdlDocuments.Add(serviceDescription);
foreach (XmlSchema nextSchema in schemas)
importer.XmlSchemas.Add(nextSchema);
ServiceContractGenerator generator = new ServiceContractGenerator();
generator.Options = ServiceContractGenerationOptions.None;
foreach (ContractDescription nextContract in importer.ImportAllContracts())
generator.GenerateServiceContractType(nextContract);
if (generator.Errors.Count != 0)
throw new Exception("Service assembly compile error: \r\n - " + string.Join("\r\n - ", generator.Errors.Select(e => e.Message)));
// Use generator.TargetCompileUnit to generate the code...
What should I do so that ServiceContractGenerator generates the web methods with straight parameters?
Upvotes: 12
Views: 1912
Reputation: 21
The accepted answer above helped (https://stackoverflow.com/a/38904854/159273).
The hint was nillable="true"
- but I am working with a WSDL that did not have nillable attributes, but still avoids request/response wrappers. Instead, it has default values (default=""
), which turns out to have the same effect.
Depending on your parameter / return value class semantics, then, you can use nullable property types, or use the [System.ComponentModel.DefaultValue()]
attribute with an appropriate value.
In my case, with another service, the request parameter type is simply string
, and that did not trigger a generated request wrapper. Only the response type needed default or nullable values, without which wrappers were generated for both request and response.
VS 2022, net8.0, net48
Upvotes: 0
Reputation: 81
I too was struggling with something similar and like @Keyto realised I was returning a DataSet in one of my methods of my WCF service. Removing this method removed the auto-generated Request/Response objects from my Reference.cs that is produced when adding a Connected Service reference in my case from a .Net5 Web API to a WCF service.
Upvotes: 0
Reputation: 71
I know it's an old question but it might help someone like me out who stumbled opon this question via google.
I had the exact same problem. I configured everything with DataContracts and used the correct settings when generating the client but it always generated message contracts.
The problem was that one of my methods returned a DataSet.
DataSet is not supported by the DataContractSerializer so Visual Studio / svcutil.exe uses the XmlSerializer. More about that can be found here: https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-schema-reference?redirectedfrom=MSDN
The solution was to remove the method that returned the DataSet.
Upvotes: 1
Reputation: 205629
When I use Visual Studio to create a WCF client ("Add Service Reference") and I click on "Advanced...", the checkbox which says "Always generate message contracts" does properly control whether the message contract objects are generated.
That's not correct. Try with the problematic WSDL from the link and you'll get the same results as using ServiceContractGenerator
. In fact, ServiceContractGenerationOptions.TypedMessages
flag (by default off) directly corresponds to the forementioned dialog option and is used (when turned on) to force creation of message contracts.
With that being said, the problem is in the WSDL and is indicated in the generated .cs
file with lines like this:
// CODEGEN: Generating message contract since element name login from namespace http://localhost/FinSwitch/ is not marked nillable
So that's the issue. Both svcutil.exe
, "Add Service Reference" dialog and ServiceContractGenerator
will not unwrap the methods when the method element or response element contains "object type" (string, base64Binary etc.) members not marked with nillable="true"
.
For instance, here is a part from the problematic WSDL:
<s:element name="DownloadFile">
<s:complexType>
<s:sequence>
<s:element type="s:string" name="login" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="password" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="fileType" maxOccurs="1" minOccurs="0"/>
<s:element type="s:dateTime" name="fileDate" maxOccurs="1" minOccurs="1"/>
<s:element type="s:boolean" name="onlyDownloadIfFileChanged" maxOccurs="1" minOccurs="1"/>
<s:element type="s:string" name="companyCode" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="category" maxOccurs="1" minOccurs="0"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DownloadFileResponse">
<s:complexType>
<s:sequence>
<s:element type="s:base64Binary" name="DownloadFileResult" maxOccurs="1" minOccurs="0"/>
</s:sequence>
</s:complexType>
</s:element>
which generates
// CODEGEN: Generating message contract since element name login from namespace http://localhost/FinSwitch/ is not marked nillable
[System.ServiceModel.OperationContractAttribute(Action="http://localhost/FinSwitch/FinSwitchWebServiceSoap/DownloadFileRequest", ReplyAction="http://localhost/FinSwitch/FinSwitchWebServiceSoap/DownloadFileResponse")]
DownloadFileResponse DownloadFile(DownloadFileRequest request);
plus message contact classes.
However, if we modify it to:
<s:element name="DownloadFile">
<s:complexType>
<s:sequence>
<s:element type="s:string" name="login" nillable="true" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="password" nillable="true" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="fileType" nillable="true" maxOccurs="1" minOccurs="0"/>
<s:element type="s:dateTime" name="fileDate" maxOccurs="1" minOccurs="1"/>
<s:element type="s:boolean" name="onlyDownloadIfFileChanged" maxOccurs="1" minOccurs="1"/>
<s:element type="s:string" name="companyCode" nillable="true" maxOccurs="1" minOccurs="0"/>
<s:element type="s:string" name="category" nillable="true" maxOccurs="1" minOccurs="0"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DownloadFileResponse">
<s:complexType>
<s:sequence>
<s:element type="s:base64Binary" name="DownloadFileResult" nillable="true" maxOccurs="1" minOccurs="0"/>
</s:sequence>
</s:complexType>
</s:element>
then the generated code is as expected
[System.ServiceModel.OperationContractAttribute(Action="http://localhost/FinSwitch/FinSwitchWebServiceSoap/DownloadFileRequest", ReplyAction="http://localhost/FinSwitch/FinSwitchWebServiceSoap/DownloadFileResponse")]
byte[] DownloadFile(string login, string password, string fileType, System.DateTime fileDate, bool onlyDownloadIfFileChanged, string companyCode, string category);
and no message contract classes.
What does all that mean? The rule is hardcoded deeply in the infrastructure (if someone is interested, here is the reference source) and cannot be changed. One can preprocess the WSDL content (afterall, it's a XML) and insert nillable="true"
where needed, but I'm not sure that can be considered to be a correct action - AFAIK, it's the service provider responsibility to provide the correct WSDL and there is no guarantee that altering it will not cause side effects.
Upvotes: 4