Customizing the Generation of Service Descriptions and Proxy Classes

The generation of the service description and proxy classes of an XML 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 WSDL document for an XML Web service — and add custom attributes to a method communicating with an XML Web service.

SDFEs are especially useful when a SOAP extension must run with both an XML 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, causing the class to run the SOAP extension.

The following are the basic steps to build an SDFE:

  1. Define the XML to add to the service description.
  2. Write code to extend the service description generation process.
  3. Write code to extend the proxy class generation process.
  4. Configure the SDFE to run on both the client and server.

The following procedures explain and illustrate each step.

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 adds an XML namespace prefix yml to the <definitions> element and the elements contained within the <yml:action> element to <operation> elements for SOAP bindings.

    <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="https://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 TraceOperationBinding class, which derives from ServiceDescriptionFormatExtension.

    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension
    [C#]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 service description XML elements generated during each point.

    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 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 TraceOperationBinding.YMLNamespace field is added to the class.

    <XmlFormatExtension("action", YMLOperationBinding.YMLNamespace, _
                        GetType(OperationBinding))> _
    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension[C#][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[C#][XmlFormatExtension("action", YMLOperationBinding.YMLNamespace,
                        typeof(OperationBinding))]
    [XmlFormatExtensionPrefix("yml", YMLOperationBinding.YMLNamespace)]
    public class YMLOperationBinding : ServiceDescriptionFormatExtension 
    
  5. Add public properties and/or fields to the class representing the XML to be added to the service description. The following code example adds a Reverse public property that is serialized into a <yml:Reverse>value</yml:Reverse> element in the service description.

    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 
    [C#]private Boolean reverse;
    [XmlElement("Reverse")]
    public Boolean Reverse 
    {
       get { return reverse; }
       set { reverse = value; }
    }
    

To extend the service description generation process

  1. Create a class deriving from SoapExtensionReflector.

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

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

    The following code example overrides the ReflectMethod.

    Public Overrides Sub ReflectMethod()
    [C#]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 XML Web service method. The following code example gets the value of the ReflectionContext property.

    Dim reflector As ProtocolReflector = ReflectionContext
    [C#]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 an XML Web service method.

    Dim attr As YMLAttribute = _
        reflector.Method.GetCustomAttribute(GetType(YMLAttribute))
    ' If the YMLAttribute has been applied to this XML 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[C#]YMLAttribute attr = (YMLAttribute)
       reflector.Method.GetCustomAttribute(typeof(YMLAttribute));
    // If the YMLAttribute has been applied to this XML 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 representing the extension point the SDFE is extending.

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

    reflector.OperationBinding.Extensions.Add(yml)
    [C#]reflector.OperationBinding.Extensions.Add(yml);
    

To extend the proxy class generation process

  1. Create a class deriving from SoapExtensionImporter.

    Public Class YMLImporter
        Inherits SoapExtensionImporter[C#]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 XML Web services created using ASP.NET, each XML Web service method maps to an operation for each supported protocol in the service description.

    Public Overrides Sub ImportMethod(ByVal metadata As _
                                      CodeAttributeDeclarationCollection)
    [C#]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 communicating with an XML Web service method. The following code example gets the value of the ImportContext property.

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

    The ImportMethod passes in one argument of type CodeAttributeDeclarationCollection, which represents the collection of attributes that are applied to the method communicating with the XML 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
    [C#]// 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);
       }
    }
    

To configure the SDFE to run with an XML Web service

  1. Install the assembly containing the SDFE in an accessible folder.

    Unless the SDFE is needed for multiple Web applications, install the SDFE in the \Bin folder of the Web application hosting the XML Web service.

  2. Add a <serviceDescriptionFormatExtensionTypes> element with an <add> element specifying the name and assembly containing the SDFE to the Web.config file for the Web application.

    The following code example configures the Sample.YMLOperationBinding SDFE to run with all XML 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 with an <add> element specifying the name and assembly of the class extending 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 XML 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 an XML Web service client

  1. Install the assembly containing the SDFE in the global assembly cache.

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

  2. Add a <serviceDescriptionFormatExtensionTypes> element with an <add> element specifying the name and assembly containing the SDFE to the Machine.config file.

    The following code example configures the Sample.YMLOperationBinding SDFE to run whenever proxy classes are generated for XML 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 with an <add> element specifying the name and assembly of the class extending 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 XML 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 communicating with the XML Web service, so if an SDFE adds an attribute residing in an assembly the client application is unaware of, a compiler error will be generated. To resolve the compiler error, add a reference to the assembly containing the attribute if using Visual Studio .NET, or add the assembly to the compiler command line if using command-line compilation.

Complete SDFE code sample

The following code example is an SDFE that extends the service description and proxy generation processes to include details about a YML SOAP extension.

Imports System
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO
Imports System.Text
Imports System.Web.Services.Configuration
Imports System.Web.Services.Description
Imports System.Xml.Serialization
Imports System.CodeDom

' The YMLAttribute allows a developer to specify that the YML SOAP
' extension run on a per-method basis.  The disabled property
' turns reversing the XML on and off. 
<AttributeUsage(AttributeTargets.Method, AllowMultiple:=False)> _
Public Class YMLAttribute
    Inherits SoapExtensionAttribute
    Dim _priority As Integer = 0
    Dim _disabled As Boolean = False

    Public Sub New()
        Me.New(False)
    End Sub
    Public Sub New(ByVal Disabled As Boolean)
        _disabled = Disabled
    End Sub
    Public Overrides ReadOnly Property ExtensionType() As Type
        Get
            Return GetType(YMLExtension)
        End Get
    End Property
    Public Overrides Property Priority() As Integer
        Get
            Return _priority
        End Get
        Set(ByVal Value As Integer)
            _priority = Value
        End Set
    End Property

    Public Property Disabled() As Boolean
        Get
            Return _disabled
        End Get
        Set(ByVal Value As Boolean)
            _disabled = Value
        End Set
    End Property
End Class

Public Class YMLExtension
    Inherits SoapExtension
    Dim _disabled As Boolean = False
    Dim oldStream As Stream
    Dim newStream As Stream

    Public Overloads Overrides Function GetInitializer(ByVal methodInfo _
                   As LogicalMethodInfo, _
                   ByVal attribute As SoapExtensionAttribute) As Object
        Dim attr As YMLAttribute = attribute
        If (Not attr Is Nothing) Then
            Return attr.Disabled
        End If
        Return False
    End Function

    Public Overloads Overrides Function GetInitializer(ByVal _
      WebServiceType As Type) As Object
        Return False
    End Function

    Public Overrides Sub Initialize(ByVal initializer As Object)
        If (TypeOf initializer Is Boolean) Then
            _disabled = CBool(initializer)
        End If
    End Sub

    Public Overrides Function ChainStream(ByVal streamref As Stream) _
      As Stream
        If (_disabled) Then
            Return CType(Me, SoapExtension).ChainStream(streamref)
        End If
        oldStream = streamref
        newStream = New MemoryStream()
        Return newStream
    End Function

    Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)
        If (_disabled) Then Return
        Select Case (message.Stage)
            Case SoapMessageStage.BeforeSerialize
                Encode(message)
            Case SoapMessageStage.AfterSerialize
                newStream.Position = 0
                Reverse(newStream, oldStream)
            Case SoapMessageStage.BeforeDeserialize
                Decode(message)
            Case SoapMessageStage.AfterDeserialize
        End Select
    End Sub

    Sub Encode(ByRef message As SoapMessage)
        message.ContentType = "text/yml"
    End Sub

    Sub Decode(ByVal message As SoapMessage)
        If (message.ContentType <> "text/yml") Then
            Throw New Exception("invalid content type:" + _
                                 message.ContentType)
        End If
        Reverse(oldStream, newStream)
        newStream.Position = 0
        message.ContentType = "text/xml"
    End Sub

    Sub Reverse(ByVal source As Stream, ByVal dest As Stream)
        Dim reader As TextReader = New StreamReader(source)
        Dim writer As TextWriter = New StreamWriter(dest)
        Dim line As String
        line = reader.ReadLine()
        While (Not line Is Nothing)
            writer.WriteLine(StrReverse(line))
            line = reader.ReadLine()
        End While
        writer.Flush()
    End Sub
End Class

' The YMLReflector class is part of the YML SDFE; it is
' called during the service description generation process.
Public Class YMLReflector
    Inherits SoapExtensionReflector
    Public Overrides Sub ReflectMethod()
        Dim reflector As ProtocolReflector = ReflectionContext
        Dim attr As YMLAttribute = _
            reflector.Method.GetCustomAttribute(GetType(YMLAttribute))
        ' If the YMLAttribute has been applied to this XML 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
            reflector.OperationBinding.Extensions.Add(yml)
        End If
    End Sub
End Class

' The YMLImporter class is part of the YML SDFE; it is called when
' a proxy class is generated for each XML Web service method the proxy
' class communicates with.  The class checks whether the service
' description contains the XML that this SDFE adds to a service
' description.  If it exists, then the YMLExtension is applied to the
' method in the proxy class.
Public Class YMLImporter
    Inherits SoapExtensionImporter
    Public Overrides Sub ImportMethod(ByVal metadata As _
                           CodeAttributeDeclarationCollection)
      Dim importer As SoapProtocolImporter = ImportContext
      ' 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
    End Sub
End Class

' The YMLOperationBinding class is part of the YML SDFE; it is the
' class that is serialized into XML and placed in the service
' description.
<XmlFormatExtension("action", YMLOperationBinding.YMLNamespace, _
                     GetType(OperationBinding)), _
 XmlFormatExtensionPrefix("yml", YMLOperationBinding.YMLNamespace)> _
Public Class YMLOperationBinding
    Inherits ServiceDescriptionFormatExtension
    Private _reverse As Boolean
    Public Const YMLNamespace As String = "https://www.contoso.com/yml"

    <XmlElement("Reverse")> _
    Public Property Reverse() As Boolean
        Get
            Return _reverse
        End Get
        Set(ByVal Value As Boolean)
            _reverse = Value
        End Set
    End Property
End Class 
[C#]using System;
using System.CodeDom;
using System.IO;
using System.Text;
using System.Web.Services.Configuration;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
using System.Xml.Serialization;
 
// The YMLAttribute allows a developer to specify that the YML SOAP
// extension run on a per-method basis.  The disabled property
// turns reversing the XML on and off. 

[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
public class YMLAttribute : SoapExtensionAttribute 
{
  int priority = 0;
  bool disabled = false;
       
  public YMLAttribute() : this(false) {}
  public YMLAttribute(bool disabled) 
  {
     this.disabled = disabled;
  }
      
  public override Type ExtensionType 
  {
    get { return typeof(YMLExtension); }
  }
  public override int Priority 
  {
    get { return priority; }
    set { priority = value; }
  }

  public bool Disabled 
  { 
    get { return disabled; }
    set { disabled = value; }
  }
}

public class YMLExtension : SoapExtension 
{
  bool disabled = false;
  Stream oldStream;
  Stream newStream;

  public override object GetInitializer(LogicalMethodInfo methodInfo,
                                        SoapExtensionAttribute attribute)
  {
    YMLAttribute attr = attribute as YMLAttribute;
    if (attr != null) return attr.Disabled;
       return false;
  }

  public override object GetInitializer(Type serviceType) 
  {
        return false;
  }

  public override void Initialize(object initializer) 
  {
     if (initializer is Boolean) disabled = (bool)initializer;
  }

  public override Stream ChainStream(Stream stream) 
  {
     if (disabled) return base.ChainStream(stream);
     oldStream = stream;
     newStream = new MemoryStream();
     return newStream;
  }

  public override void ProcessMessage(SoapMessage message) 
  {
    if (disabled) return;
    switch (message.Stage) 
    {
      case SoapMessageStage.BeforeSerialize:
        Encode(message);
        break;
      case SoapMessageStage.AfterSerialize:
        newStream.Position = 0;
        Reverse(newStream, oldStream);
        break;
      case SoapMessageStage.BeforeDeserialize:
        Decode(message);
        break;
      case SoapMessageStage.AfterDeserialize:
        break;
    }
  }        
  void Encode(SoapMessage message) 
  {
     message.ContentType = "text/yml";
  }

  void Decode(SoapMessage message) 
  {
   if (message.ContentType != "text/yml") 
     throw new Exception("invalid content type:" + message.ContentType);
   Reverse(oldStream, newStream);
   newStream.Position = 0;
   message.ContentType = "text/xml";
  }

  void Reverse(Stream from, Stream to) 
  {
    TextReader reader = new StreamReader(from);
    TextWriter writer = new StreamWriter(to);
    string line;
    while ((line = reader.ReadLine()) != null) 
    {
      StringBuilder builder = new StringBuilder();
      for (int i = line.Length - 1; i >= 0; i--) 
      {
        builder.Append(line[i]);
      }
      writer.WriteLine(builder.ToString());
    }
    writer.Flush();
  }
}

// The YMLReflector class is part of the YML SDFE; it is
// called during the service description generation process.
public class YMLReflector : SoapExtensionReflector 
{
  public override void ReflectMethod() 
  {
    ProtocolReflector reflector = ReflectionContext;
    YMLAttribute attr = (YMLAttribute)reflector.Method.GetCustomAttribute(
                        typeof(YMLAttribute));
    // If the YMLAttribute has been applied to this XML Web service
    // method, adds the XML defined in the YMLOperationBinding class.
    if (attr != null) 
    {
      YMLOperationBinding yml = new YMLOperationBinding();
      yml.Reverse = !(attr.Disabled);
      reflector.OperationBinding.Extensions.Add(yml);
    }
  }
}
  
// The YMLImporter class is part of the YML SDFE; it is called when
// a proxy class is generated for each XML Web service method the proxy
// class communicates with.  The class checks whether the service
// description contains the XML that this SDFE adds to a service
// description.  If it exists, then the YMLExtension is applied to the
// method in the proxy class.
public class YMLImporter : SoapExtensionImporter 
{
  public override void ImportMethod(CodeAttributeDeclarationCollection
                                    metadata)
 {
    SoapProtocolImporter importer = ImportContext;
   // 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);
     }
   }
 }
}

// The YMLOperationBinding class is part of the YML SDFE; it is the
// class that is serialized into XML and placed in the service
// description.
[XmlFormatExtension("action", YMLOperationBinding.YMLNamespace,
                    typeof(OperationBinding))]
[XmlFormatExtensionPrefix("yml", YMLOperationBinding.YMLNamespace)]
public class YMLOperationBinding : ServiceDescriptionFormatExtension 
{
   private Boolean reverse;
   public const string YMLNamespace = "https://www.contoso.com/yml";

   [XmlElement("Reverse")]
   public Boolean Reverse 
   {
     get { return reverse; }
     set { reverse = value; }
   }
}

See Also

Altering the SOAP Message Using SOAP Extensions | XmlFormatExtensionAttribute | XmlFormatExtensionPrefixAttribute | XmlFormatExtensionPointAttribute