wcf icon indicating copy to clipboard operation
wcf copied to clipboard

dotnet svcutil: generate classes from XSD

Open Ramin2c opened this issue 6 years ago • 6 comments

Hi I wonder if it is possible to generate only classes (and not the client) with the dotnet svcutil from a set of XSD files. The same way we used to do with the old xsd command line provided the /c switch. The reason behind this: in our project we need to extract data types from a set of provided XSD files first and write the code for the webservice client manually later.

Bests Ramin

Ramin2c avatar Jan 31 '19 11:01 Ramin2c

@Ramin2c This sounds similar to the original svcutil's /dataContractOnly argument. Would adding that option to dotnet-svcutil give you what you're looking for?

dasetser avatar Feb 14 '19 22:02 dasetser

@dasetser Yes, that sounds good!

Ramin2c avatar Feb 21 '19 13:02 Ramin2c

Has this been implemented yet? if so, how do I use it? I'm in dire need of this 😢 I'm currently using version 6.0.0 of wcf

leocb avatar Feb 24 '22 18:02 leocb

Has this been implemented yet? if so, how do I use it? I'm in dire need of this 😢 I'm currently using version 6.0.0 of wcf

I don't know about the progress on the issue but to get around this I had to do a lot of manual work :D if you want, I can look into my old notes, see if i can find and share them with you!

Ramin2c avatar Feb 25 '22 07:02 Ramin2c

@Ramin2c Yes please, if you can, that would be a great help!

leocb avatar Mar 04 '22 22:03 leocb

@leocb Here it is. The original document was written for a company I worked for before and it was meant to be used for a specific API and I had to cut them out. So, that's why my instructions might seem little messy and inrelated but I hope it still can help you get an idea of how it should:

  1. Required modification to the generated code Like said before the generated client code might be incomplete. One indication of an incomplete code is empty classes being generated. The best way to check this is to compare generated classes with the equivalent definitions in the WSDL. If things are missing they ought to be added manually. In this case, the aforementioned XSD files can also be of help. XSD files are basically presenting a structured data in form of XML. These xml definitions can be easily converted to .net classes in one go using the xsd command from the “Command prompt for Visual Studio” or any other shell command from the location of the xsd command, i.e.: %programfiles(x86)%\Microsoft SDKs\Windows{ver}\bin{FXVer} Tools The details of how to generate classes from XSD files are given later but it is enough to know that the generated classes can be taken as a model to see how incomplete classes should look like, what relationships exist between different classes or even which namespaces are used for different actions in the web service. The following modifications after all might be needed:
  2. Write code for missing classes and use them in the right places; one typical example is the base classes like BaseRequestType and BaseResponseType.
  3. Mark the generated classes with proper attributes (DataContactAttribute, MessageContractAttribute or CollectionDataContractAttribute) and their members with MessageBodyMemberAttribute
  4. if needed. Also consider implementing the IXmlSerializable interface for manual parsing of the data. This might be helpful in some special cases when the complex data cannot be parsed properly using the previous attributes or more control over the deserialization is required. The details about the usage of these attributes and custom data deserialization are given in the next chapter.
  5. The client constructor should always do the following: a. Configure the endpoint implemented as “static partial” method and can be left empty in most of the cases and is only kept for compatibility. b. Setting the client certificate in case it is required by the server: this.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerOrChainTrust; this.ClientCredentials.ClientCertificate.Certificate = ; c. Create the extra headers. Refer to the (1) to see which headers are mandatory for each service but in general the followings are needed: i. FromHeader ii. ActionHeader iii. MessageHeader iv. ToHeader v. SecurityHeader d. The created headers are added to the outgoing SOAP messages through an inspector class belonging to an instance of IEndpointBehavior which the class needs to have been implemented. e. Return a proper binding for the endpoint The GetBindingForEndpoint method is automatically implemented but in case client certificate is required the httpBindingElement instance should be replaced by an instance of the httpsBindingElement with its RequireClientCertificate property set to true. Also the followings are recommended to make the generated code easier to understand and also to make the transfer easier:
  6. Since the tool generate all classes as “partial” it is possible to write all the new code in a separate files, this includes the required types and the required modifications for web methods.
  7. Since more than one web service might be imported into the same project, it is a good practice to import each into a different namespace and also rename the automatically generated “Reference.cs” file to something more meaningful.
  8. Import the service as Internal to prevent access to the client class as a whole to the external assemblies. Instead, create new public client classes to act as intermediate channel to access the service.
  9. The tool generates many constructor overloads. For the sake of simplicity, mark those that are not needed as private or just remove them to have only one internal constructor available at the end. One typical signature for constructors in the client classes is the next one: (X509Certificate2 clientCertificate, string userName, string passWord)
  10. The import tool tends name the interface with no “I” in the beginning. For more readability rename all the interfaces to start with “I”.
  11. Avoid explicit interface implementation in general for simplicity. Otherwise to reach an implemented interface member, the interface name should always be used in the middle.
  12. The import tool generates sometimes an alternative signature with no input parameters for some methods that do not belong to the interface. It is recommended to make them private to avoid confusion. See below for an example: System.Threading.Tasks.Task<NS.GetObjectResponse> GetObjectAsync(NS.TheRequest request){…} // This method exists in the interface

private System.Threading.Tasks.Task<NS.GetObjectResponse> GetObjectAsync() {…} // This method does not exist in the interface but is added to the implementation

  1. ”string” and ”hexBindary” data types are almost equivalent can be used interchangeably. It is recommended to replace ”hexBindry” by ”string” wherever the it is used.

  2. Data deserialization in WCF Automatic data serialization and deserialization is enabled by the usage of contract attributes as follows: a. MessageContractAttribute To be applied on the types that appear as the child elements of the SOAP body. Note that it should be used only for the direct child elements and not on child elements in another child element. b. MessageBodyMemberAttribute Maps an element in the SOAP body to a class member. If the name of the member is not the same as the member’s, the ”Name” attribute can be used to indicate the correct name. This is useful for example when the SOAP element has a “-“ in its name which is illegal to use in C# language naming convention. Also, note the usage of the “Order”. It is particularly important to set a proper order if the default order is overridden for any reason. The default serialization/deserialization order is as follows:

  3. If the data contract type is a derived class, the members of the base type will always be serialized first.

  4. If no order is specified, the members will be serialized in alphabetical order based on an ordinal comparison.

  5. Any members with a specified order will be serialized from least to greatest. If two members share the same order, the serialization order will be determined by Rule #2. It is recommended to use the “Order” attribute as it will produce more code readability anyway. c. CollectionDataContractAttribute d. Can be applied to collection types and indicates that the SOAP message should be deserialized as a collection. The limitation is the type that is marked with this attribute cannot include members of MessageBodyMemberAttribute. The workaround to this problem is to enable custom serialization and deserialization of the SOAP message by (preferably explicitly) implementing the IXmlSerializable interface instead. The class that implements the interface then is responsible for mapping the XML elements to its members during the deserialization and vice versa in case of serialization. Note that the incoming XML message is the inner body of the SOAP message (i.e. does not include a root element) and thus it is a good practice to manually add some sort of root element before parsing the content. See the below code as an example: Company Specific code, I cannot share but let me know if you need this example and I will try to rewrite. I cannot promise though, sry.

  6. Generating schema classes from the xsd file(s) The xsd command is used to compile the xsd files and generate a C# schema classes like below: xsd import/soap-12.xsd import/ws-addr.xsd import/ws-addressing-10.xsd …. euicc.root.xsd /classes /namespace: /out: It is better to generate all the classes at once in one command, otherwise all the dependencies have to be injected into command every time xsd command is executed to generate one class. The order is not important except that classes under the import directory need to be taken first. Note the usage of the /classes (short form /c) switch which forces the schema generation for all the classes. The command upon the execution returns some warnings for Schema Validation like below: . . Schema validation warning: The complexType 'BaseRequestType' has already been declared. Line 26, position 6. Schema validation warning: The complexType 'BaseResponseType' has already been declared. Line 45, position 6. Schema validation warning: The complexType 'BaseNotificationType' has already been declared. Line 14, position 6. Schema validation warning: The complexType 'BaseRequestType' has already been declared. Line 26, position 6. Schema validation warning: The complexType 'BaseResponseType' has already been declared. Line 45, position 6. . . Warning: Schema could not be validated. Class generation may fail or may produce incorrect results. But these basically mean that the tool has found some duplicate types and they are being ignored after the first time of generation.

  7. Debugging and tracing There are a couple of things to consider when debugging the code:

  8. The auto generated client code is marked with [debuggerstepthroughattribute] on most if its parts which ignore the breakpoints and stepping into during the debugging process. It is enough to remove the attributes from the desired part of the code to enable proper debugging. Note that the attribute can be applied to both class and member levels.

  9. Like said before in chapter 5, an inspector class is used to modify the ongoing SOAP message by adding the necessary headers. The same class is useful also for debugging purposes and particularly for checking the content of incoming and outgoing SOAP messages. Set a breakpoint on BeforeSendRequest and AfterReceiveReply functions to check respectively outgoing and incoming messages. The content of the messages can also be printed out to the console or diagnostics window.

  10. Other Notes

  11. To learn more about serialization in WCF a good article is available here: http://jeffbarnes.net/blog/post/2007/05/08/wcf-serialization-order-in-data-contracts.aspx

  12. Some renaming can make the auto generated code more readable. Warning: Pay extra attention when renaming a type. You might need to add or modify the WrapperName propery of the attached MessageContractAttribute as well to keep the updated type still coherent with the WSDL.

  13. Be careful when updating the WCF Web Service Reference Provider from Visual Studio (Right click the auto generated service subfolder under the “Connected Services”) as it might override the changes.

Ramin2c avatar Mar 07 '22 09:03 Ramin2c