Walkthrough: Customizing the Generation of Service Descriptions and Proxy Classes

This topic is specific to a legacy technology. XML Web services and XML Web service clients should now be created using Windows Communication Foundation.

The generation of the service description and proxy classes of a Web service created using ASP.NET can be extended through the creation and installation of a service description format extension (SDFE). Specifically, an SDFE can add XML elements to the service description — the Web Services Description Language (WSDL) document for a Web service — and add custom attributes to a method that communicates with a Web service.

SDFEs are especially useful when a SOAP extension must run with both a Web service and its clients; by default, no information about SOAP extensions is placed in either the service description or proxy classes generated for it. An example of a SOAP extension that must run on both the client and server is an encryption SOAP extension. If an encryption SOAP extension executes on the server to encrypt the SOAP response, the client must have the SOAP extension running to decrypt the message. An SDFE can add elements to the service description to inform clients that a SOAP extension must run, and the SDFE can extend the proxy class generation process to add a custom attribute to the proxy class, which causes the class to run the SOAP extension. This walkthrough demonstrates the following tasks:

  1. Defining the XML to add to the service description.

  2. Creating an SDFE class by deriving from the ServiceDescriptionFormatExtension class.

  3. Writing code to extend the service description generation process.

  4. Writing code to extend the proxy class generation process.

  5. Configuring the SDFE to run on both the client and server.

Defining the XML and Creating the SDFE Class

The code samples in this walkthrough involve a ServiceDescriptionFormateExtension class YMLOperationBinding for a SoapExtension class YMLExtension. The full code appears in the topic How to: Customize the Generation of Service Descriptions and Proxy Classes (Sample Code).

To define the XML to add to the service description

  1. Decide on the XML to add to the service description.

    The following code example is the portion of a service description to which the sample SDFE adds XML elements. Specifically, the sample SDFE declares an XML namespace prefix yml in the root definitions element of a WSDL document, and applies that namespace to the yml:action element (and child elements) that appear in binding operation elements.

    <definitions ...
      xmlns:yml="https://www.contoso.com/yml" >
      ...
      <binding name="HelloWorldSoap" type="s0:HelloWorldSoap">
        <soap:binding transport="https://schemas.xmlsoap.org/soap/http"
                    style="document" /> 
          <operation name="SayHello">
            <soap:operation soapAction="http://tempuri.org/SayHello"
                        style="document" />
            <yml:action>
              <yml:Reverse>true</yml:Reverse> 
            </yml:action>
          </operation>
          ...
      </binding>
      ... 
    </definitions>
    
  2. Create a class that derives from ServiceDescriptionFormatExtension.

    When using Visual Studio .NET, add a reference to the System.Web.Services assembly. Also add a using or Imports statement for the System.Web.Services.Description namespace to the file.

    The following code example creates the YMLOperationBinding class, which derives from ServiceDescriptionFormatExtension .

    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension
    
    public class YMLOperationBinding : ServiceDescriptionFormatExtension
    
  3. Apply an XmlFormatExtensionAttribute to the class.

    This attribute specifies the stage of the service description generation process, known as an extension point, at which the SDFE runs. The following table lists the defined extension points and the WSDL XML elements generated during each point. For the extension point specified, the corresponding WSDL element becomes the parent of the element being added.

    Extension point Description

    ServiceDescription

    Corresponds to the root definitions element of a WSDL document.

    Types

    Corresponds to the types element enclosed by the root definitions element.

    Binding

    Corresponds to the binding element enclosed by the root definitions element.

    OperationBinding

    Corresponds to the operation element enclosed by the binding element.

    InputBinding

    Corresponds to the input element enclosed by the operation element.

    OutputBinding

    Corresponds to the output element enclosed by the operation element.

    FaultBinding

    Corresponds to the fault element enclosed by the operation element.

    Port

    Corresponds to the port element enclosed by the service element.

    Operation

    Corresponds to the operation element enclosed by the portType element.

    When applying an XmlFormatExtensionAttribute to the class, you also specify the XML element name and the XML namespace to contain the XML elements to add to the service description.

    The following code example specifies that the YMLOperationBinding SDFE adds an XML element named <action xmlns="https://www.contoso.com/yml"> to the service description during the OperationBinding extension point. For this sample, the XML namespace https://www.contoso.com/yml is specified later when the YMLOperationBinding.YMLNamespace field is added to the class.

    <XmlFormatExtension("action", YMLOperationBinding.YMLNamespace, _
                        GetType(OperationBinding))> _
    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension
    
    [XmlFormatExtension("action", YMLOperationBinding.YMLNamespace,
                        typeof(OperationBinding))]
    public class YMLOperationBinding : ServiceDescriptionFormatExtension
    
  4. Optionally, apply an XmlFormatExtensionPrefixAttribute to the class to associate the XML namespace prefix with the XML namespace used by the SDFE.

    The following code example specifies that the yml XML namespace prefix is associated with the https://www.contoso.com/yml namespace in the definitions element of the service description. Additionally, the prefix is used in elements added by the SDFE instead of the namespace. Therefore, the XML element added to the service description in step 3 now uses the namespace prefix and thus the element added is <yml:action> instead of <action xmlns="https://www.contoso.com/yml">.

    <XmlFormatExtension("action", YMLOperationBinding.YMLNamespace, _
                        GetType(OperationBinding)), _
     XmlFormatExtensionPrefix("yml", YMLOperationBinding.YMLNamespace)> _
    Public Class YMLOperationBinding
         Inherits ServiceDescriptionFormatExtension
    
    [XmlFormatExtension("action", YMLOperationBinding.YMLNamespace,
                        typeof(OperationBinding))]
    [XmlFormatExtensionPrefix("yml", YMLOperationBinding.YMLNamespace)]
    public class YMLOperationBinding : ServiceDescriptionFormatExtension 
    
  5. Add public properties and fields to the class that represents the XML to be added to the WSDL document. The following code example adds a Reverse public property that is serialized into a <yml:Reverse>value</yml:Reverse> element in the WSDL.

    Private _reverse As Boolean
    <XmlElement("Reverse")> _
    Public Property Reverse() As Boolean
       Get
         Return _reverse
       End Get
       Set(ByVal Value As Boolean)
          _reverse = Value
       End Set
    End Property 
    
    private Boolean reverse;
    [XmlElement("Reverse")]
    public Boolean Reverse 
    {
       get { return reverse; }
       set { reverse = value; }
    }
    

Extending the Generation of the Service Description and Client Proxy

To extend the WSDL generation process, derive a class from the SoapExtensionReflector class. To extend the client proxy generation process, derive a class from the SoapExtensionImporter class.

To extend the service description generation process

  1. Create a class that derives from SoapExtensionReflector.

    The following code example creates the TraceReflector class, which derives from SoapExtensionReflector .

    Public Class YMLReflector
        Inherits SoapExtensionReflector
    
    public class YMLReflector : SoapExtensionReflector
    
  2. Override the ReflectMethod method, which is called during the service description generation for each Web service method.

    The following code example overrides the ReflectMethod.

    Public Overrides Sub ReflectMethod()
    
    public override void ReflectMethod()
    
  3. Get the value of the ReflectionContext property of the SoapExtensionReflector class to get an instance of ProtocolReflector.

    The instance of ProtocolReflector provides details about the WSDL generation process for the current Web service method. The following code example gets the value of the ReflectionContext property.

    Dim reflector As ProtocolReflector = ReflectionContext
    
    ProtocolReflector reflector = ReflectionContext;
    
  4. Add code to populate the SDFE.

    The following code example adds the XML defined by the SDFE to the service description if the YMLAttribute is applied to a Web service method.

    Dim attr As YMLAttribute = _
        reflector.Method.GetCustomAttribute(GetType(YMLAttribute))
    ' If the YMLAttribute has been applied to this Web service
    ' method, adds the XML defined in the YMLOperationBinding class.
    If (Not attr Is Nothing) Then
       Dim yml As YMLOperationBinding = New YMLOperationBinding()
       yml.Reverse = Not attr.Disabled
    
    YMLAttribute attr = (YMLAttribute)
       reflector.Method.GetCustomAttribute(typeof(YMLAttribute));
    // If the YMLAttribute has been applied to this Web service 
    // method, adds the XML defined in the YMLOperationBinding class.
    if (attr != null) {
       YMLOperationBinding yml = new YMLOperationBinding();
       yml.Reverse = !(attr.Disabled);
    
  5. Add the SDFE to the Extensions collection of the property that represents the extension point the SDFE is extending.

    The following code example adds the YmlOperationBinding SDFE to the OperationBinding extension point.

    reflector.OperationBinding.Extensions.Add(yml)
    
    reflector.OperationBinding.Extensions.Add(yml);
    

To extend the proxy class generation process

  1. Create a class that derives from SoapExtensionImporter.

    Public Class YMLImporter
        Inherits SoapExtensionImporter
    
    public class YMLImporter : SoapExtensionImporter
    
  2. Override the ImportMethod method.

    The ImportMethod is called during proxy class generation for each operation defined in a service description. For Web services created using ASP.NET, each Web service method maps to an operation for each supported protocol in the service description.

    Public Overrides Sub ImportMethod(ByVal metadata As _
                                      CodeAttributeDeclarationCollection)
    
    public override void ImportMethod(CodeAttributeDeclarationCollection
                                      metadata)   
    
  3. Get the value of the ImportContext property of SoapExtensionImporter to get an instance of SoapProtocolImporter.

    The instance of SoapProtocolImporter provides details about the code generation process for the current method that communicates with a Web service method. The following code example gets the value of the ImportContext property.

    Dim importer As SoapProtocolImporter = ImportContext
    
    SoapProtocolImporter importer = ImportContext;  
    
  4. Add code to apply or modify attributes to a method in the proxy class that is communicating with a Web service.

    The ImportMethod passes in one argument of type CodeAttributeDeclarationCollection, which represents the collection of attributes that are applied to the method that communicates with the Web service method. The following code example adds a YMLAttribute to the collection, which causes the YML SOAP extension to run with the method when the service description contains the appropriate XML.

    ' Checks whether the XML specified in the YMLOperationBinding is in the
    ' service description.
    Dim yml As YMLOperationBinding = _
      importer.OperationBinding.Extensions.Find( _
      GetType(YMLOperationBinding))
    If (Not yml Is Nothing) Then
       ' Only applies the YMLAttribute to the method when the XML should
       ' be reversed.
       If (yml.Reverse) Then
          Dim attr As CodeAttributeDeclaration = New _
            CodeAttributeDeclaration(GetType(YMLAttribute).FullName)
          attr.Arguments.Add(New CodeAttributeArgument(New _
               CodePrimitiveExpression(True)))
          metadata.Add(attr)
       End If
    End If
    
    // Checks whether the XML specified in the YMLOperationBinding is
    // in the service description.
    YMLOperationBinding yml = (YMLOperationBinding)
       importer.OperationBinding.Extensions.Find(
       typeof(YMLOperationBinding));
    if (yml != null)
    {
       // Only applies the YMLAttribute to the method when the XML should
       // be reversed.
       if (yml.Reverse)
       {
         CodeAttributeDeclaration attr = new
            CodeAttributeDeclaration(typeof(YMLAttribute).FullName);
         attr.Arguments.Add(new CodeAttributeArgument(new
            CodePrimitiveExpression(true)));
         metadata.Add(attr);
       }
    }
    

Configuring the SDFE

To configure the SDFE requires editing configuration files on both the Web service and client.

To configure the SDFE to run with a Web service

  1. Install the assembly that contains the SDFE in an accessible folder.

    Unless the SDFE is required for multiple Web applications, install the SDFE in the \bin folder of the Web application hosting the Web service.

  2. Add a <serviceDescriptionFormatExtensionTypes> Element element with an add element and specify the name and assembly that contains the SDFE to the Web.config file for the Web application.

    The following code example configures the Sample.YMLOperationBinding SDFE to run with all Web services affected by the Web.config file. The complete add element should be on one line.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>
             <add type="Sample.YMLOperationBinding,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </serviceDescriptionFormatExtensionTypes>
       </webServices>
    </system.web>
    
  3. Add a <soapExtensionReflectorTypes> Element element with an add element and specify the name and assembly of the class that extends the service description generation process to the Web.config file for the Web application.

    The following code example configures the Sample.YMLReflector to run with all Web services affected by the Web.config file. The complete add element should be on one line.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>
             <add type="Sample.YMLOperationBinding,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </serviceDescriptionFormatExtensionTypes>
          <soapExtensionReflectorTypes>
             <add type="Sample.YMLReflector,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </soapExtensionReflectorTypes>
       </webServices>
    </system.web>
    

To configure the SDFE to run with a Web service client

  1. Install the assembly that contains the SDFE in the global assembly cache.

    To be installed, the assembly must be strong-named. For more information about creating a strong-named assembly, see Creating and Using Strong-Named Assemblies. For more information about installing an assembly, see Installing an Assembly into the Global Assembly Cache.

  2. Add a <serviceDescriptionFormatExtensionTypes> Element element with an add element and specify the name and assembly that contains the SDFE to the Machine.config file.

    The following code example configures the Sample.YMLOperationBinding SDFE to run whenever proxy classes are generated for Web services on the machine.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>
             <add type="Sample.YMLOperationBinding,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </serviceDescriptionFormatExtensionTypes>
       </webServices>
    </system.web>
    
  3. Add a <soapExtensionImporterTypes> Element element with an add element and specify the name and assembly of the class that extends the proxy class generation process to the Machine.config file.

    The following code example configures the Sample.YMLImporter to run whenever proxy classes are generated for Web services on the machine.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>
             <add type="Sample.YMLOperationBinding,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </serviceDescriptionFormatExtensionTypes>
          <soapExtensionImporterTypes>
             <add type="Sample.YMLImporter,Yml,
                  Version=1.0.0.0,Culture=neutral,
                  PublicKeyToken=6e55c64c6b897b30"/>
          </soapExtensionImporterTypes>
       </webServices>
    </system.web>
    

    Note

    The method generated in the proxy class is used by a client application that communicates with the Web service, so if an SDFE adds an attribute that resides in an assembly of which the client application is not notified, a compiler error is generated. To resolve the compiler error, add a reference to the assembly that contains the attribute if using Visual Studio .NET, or add the assembly to the compiler command line if using command-line compilation.

See Also

Tasks

Walkthrough: Customizing the Generation of Service Descriptions and Proxy Classes

Reference

XmlFormatExtensionAttribute
XmlFormatExtensionPrefixAttribute
XmlFormatExtensionPointAttribute

Concepts

SOAP Message Modification Using SOAP Extensions