Personalizzazione della generazione di descrizioni dei servizi e classi proxy

La generazione di descrizioni dei servizi e classi proxy di un servizio Web XML creato con ASP.NET può essere estesa alla creazione e installazione di un'estensione di formato di descrizione servizio (SDFE, Service Description Format Extension). In particolare, con una SDFE è possibile aggiungere elementi XML alla descrizione del servizio (il documento WSDL per un servizio Web XML) e aggiungere attributi personalizzati a un metodo destinato alla comunicazione con un servizio Web XML.

L'uso delle SDFE risulta particolarmente utile quando occorre eseguire un'estensione SOAP sia con un servizio Web XML che con i relativi client. Per impostazione predefinita, nessuna informazione sulle estensioni SOAP viene inserita né nella descrizione del servizio né nelle classi proxy appositamente generate. Un esempio di estensione SOAP destinata all'esecuzione sia sul client che sul server è l'estensione SOAP di crittografia. Se un'estensione SOAP di crittografia viene eseguita sul server per crittografare la risposta SOAP, è necessario che anche sul client sia in esecuzione l'estensione SOAP per decrittografare il messaggio. Una SDFE può aggiungere elementi alla descrizione del servizio, in modo che i client siano informati della necessità di eseguire un'estensione SOAP, e può estendere il processo di generazione delle classi proxy perché a queste venga aggiunto un attributo personalizzato che induca la classe a eseguire l'estensione SOAP.

Di seguito sono riportate le operazioni di base per la creazione di una SDFE.

  1. Definire l'XML da aggiungere alla descrizione del servizio.
  2. Scrivere codice tramite cui estendere il processo di generazione della descrizione del servizio.
  3. Scrivere codice tramite cui estendere il processo di generazione della classe proxy.
  4. Configurare la SDFE in modo che venga eseguita sia sul client che sul server.

Nella procedura che segue viene spiegato e illustrato ciascun passaggio.

Per definire l'XML da aggiungere alla descrizione del servizio

  1. Stabilire quale XML aggiungere alla descrizione del servizio.

    Nell'esempio di codice che segue viene riportata la parte di una descrizione di un servizio in cui la SDFE aggiunge elementi XML. In particolare, con la SDFE di esempio vengono aggiunti un prefisso dello spazio dei nomi XML yml all'elemento <definitions> e gli elementi contenuti in <yml:action> agli elementi <operation> per le associazioni SOAP.

    <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. Creare una classe derivata da ServiceDescriptionFormatExtension.

    Se si utilizza Visual Studio .NET, aggiungere un riferimento all'assembly System.Web.Services. Aggiungere al file anche un'istruzione using o Imports per lo spazio dei nomi System.Web.Services.Description. Nell'esempio di codice che segue viene creata la classe TraceOperationBinding, che deriva da ServiceDescriptionFormatExtension.

    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension
    [C#]
    public class YMLOperationBinding : ServiceDescriptionFormatExtension
    
  3. Applicare un XmlFormatExtensionAttribute alla classe.

    Con tale attributo si specifica la fase del processo di generazione della descrizione del servizio nota come punto di estensione, in cui viene eseguita la SDFE. Nella tabella che segue vengono elencati i punti di estensione definiti e gli elementi XML di descrizione del servizio generati ad ogni punto.

    Punto di estensione Descrizione
    ServiceDescription Corrisponde all'elemento <definitions> principale di un documento WSDL.
    Tipi Corrisponde all'elemento <types> incluso nell'elemento <definitions> principale.
    Binding Corrisponde all'elemento <binding> incluso nell'elemento <definitions> principale.
    OperationBinding Corrisponde all'elemento <operation> incluso nell'elemento <binding>.
    InputBinding Corrisponde all'elemento <input> incluso nell'elemento <operation>.
    OutputBinding Corrisponde all'elemento <output> incluso nell'elemento <operation>.
    FaultBinding Corrisponde all'elemento <fault> incluso nell'elemento <operation>.
    Port Corrisponde all'elemento <port> incluso nell'elemento <service>.
    Operation Corrisponde all'elemento <operation> incluso nell'elemento <portType>.

    Quando si applica un XmlFormatExtensionAttribute alla classe, si specifica anche l'elemento XML e lo spazio dei nomi XML che conterrà gli elementi XML da aggiungere alla descrizione del servizio.

    Nell'esempio di codice che segue si specifica che la SDFE YMLOperationBinding aggiunge un elemento denominato <action xmlns="https://www.contoso.com/yml"> alla descrizione del servizio durante il punto di estensione OperationBinding. Nell'esempio lo spazio dei nomi https://www.contoso.com/yml viene specificato in seguito, quando si aggiunge il campo TraceOperationBinding.YMLNamespace alla classe.

    <XmlFormatExtension("action", YMLOperationBinding.YMLNamespace, _
                        GetType(OperationBinding))> _
    Public Class YMLOperationBinding
        Inherits ServiceDescriptionFormatExtension 
    [C#]
    [XmlFormatExtension("action", YMLOperationBinding.YMLNamespace,
                        typeof(OperationBinding))]
    public class YMLOperationBinding : ServiceDescriptionFormatExtension
    
  4. È anche possibile applicare un XmlFormatExtensionPrefixAttribute alla classe per associare il prefisso dello spazio dei nomi XML allo spazio dei nomi XML utilizzato dalla SDFE.

    Nell'esempio di codice che segue si specifica che il prefisso dello spazio dei nomi XML yml è associato allo spazio dei nomi https://www.contoso.com/yml nell'elemento <definitions> della descrizione del servizio. Il prefisso, inoltre, viene utilizzato negli elementi aggiunti dalla SDFE in luogo dello spazio dei nomi. Pertanto, l'elemento XML aggiunto alla descrizione del servizio nel passaggio 3 utilizza adesso il prefisso dello spazio dei nomi e l'elemento aggiunto, di conseguenza, è <yml:action> anziché <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. Aggiungere alla classe proprietà e/o campi pubblici che rappresentino l'XML da aggiungere alla descrizione del servizio. Nell'esempio di codice che segue viene aggiunta una proprietà pubblica Reverse che viene serializzata in un elemento <yml:Reverse>value</yml:Reverse> nella descrizione del servizio.

    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; }
    }
    

Per estendere il processo di generazione della descrizione del servizio

  1. Creare una classe derivata da SoapExtensionReflector.

    Nell'esempio di codice che segue viene creata la classe TraceReflector, che deriva da SoapExtensionReflector.

    Public Class YMLReflector
        Inherits SoapExtensionReflector
    [C#]
    public class YMLReflector : SoapExtensionReflector
    
  2. Eseguire l'override del metodo ReflectMethod, che viene chiamato durante la generazione della descrizione del servizio per ogni metodo del servizio Web XML.

    Nell'esempio di codice che segue si esegue l'override di ReflectMethod.

    Public Overrides Sub ReflectMethod()
    [C#]
    public override void ReflectMethod()
    
  3. Recuperare il valore della proprietà ReflectionContext della classe SoapExtensionReflector per ottenere un'istanza di ProtocolReflector.

    Nell'istanza di ProtocolReflector vengono forniti dettagli sul processo di generazione WSDL per il metodo corrente del servizio Web XML. Nell'esempio di codice che segue viene recuperato il valore della proprietà ReflectionContext.

    Dim reflector As ProtocolReflector = ReflectionContext
    [C#]
    ProtocolReflector reflector = ReflectionContext;
    
  4. Aggiungere codice per compilare la SDFE.

    Nell'esempio di codice che segue viene aggiunto l'XML definito dalla SDFE alla descrizione del servizio, se YMLAttribute è applicato a un metodo del servizio Web XML.

    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. Aggiungere la SDFE all'insieme Extensions della proprietà che rappresenta il punto di estensione in cui la SDFE sta effettuando l'estensione.

    Nell'esempio di codice che segue viene aggiunta la SDFE TraceOperationBinding al punto di estensione OperationBinding.

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

Per estendere il processo di generazione della classe proxy

  1. Creare una classe derivata da SoapExtensionImporter.

    Public Class YMLImporter
        Inherits SoapExtensionImporter 
    [C#]
    public class YMLImporter : SoapExtensionImporter
    
  2. Eseguire l'override del metodo ImportMethod.

    ImportMethod viene chiamato durante la generazione della classe proxy per ogni operazione definita in una descrizione del servizio. Per i servizi Web XML creati con ASP.NET, ogni metodo del servizio Web XML esegue il mapping a un'operazione per ogni protocollo supportato indicato nella descrizione del servizio.

    Public Overrides Sub ImportMethod(ByVal metadata As _
                                      CodeAttributeDeclarationCollection)
    [C#]
    public override void ImportMethod(CodeAttributeDeclarationCollection
                                      metadata)   
    
  3. Recuperare il valore della proprietà ImportContext di SoapExtensionImporter per ottenere un'istanza di SoapProtocolImporter.

    Nell'istanza di SoapProtocolImporter vengono forniti dettagli sul processo di generazione del codice per il metodo corrente che comunica con un metodo del servizio Web XML. Nell'esempio di codice che segue viene recuperato il valore della proprietà ImportContext.

    Dim importer As SoapProtocolImporter = ImportContext
    [C#]
    SoapProtocolImporter importer = ImportContext;  
    
  4. Aggiungere codice per modificare o applicare attributi a un metodo nella classe proxy che comunica con un servizio Web XML.

    Con ImportMethod viene passato un argomento di tipo CodeAttributeDeclarationCollection, che rappresenta l'insieme di attributi applicati al metodo che comunica con il metodo del servizio Web XML. Nell'esempio di codice che segue viene aggiunto un YMLAttribute all'insieme, pertanto l'estensione SOAP YML verrà eseguita con il metodo quando la descrizione del servizio contiene l'XML appropriato.

    ' 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);
       }
    }
    

Per configurare la SDFE in modo che venga eseguita con un servizio Web XML

  1. Installare l'assembly contenente la SDFE in una cartella accessibile.

    A meno che la SDFE non venga utilizzata per più applicazioni Web, installarla nella cartella \Bin dell'applicazione Web che fornisce il servizio Web XML.

  2. Aggiungere un elemento <serviceDescriptionFormatExtensionTypes> con un elemento <add> che specifichi il nome e l'assembly contenente la SDFE nel file Web.config dell'applicazione Web.

    Nell'esempio di codice che segue viene configurata la SDFE Sample.YMLOperationBinding in modo che venga eseguita con tutti i servizi Web XML che fanno capo al file Web.config. L'intero elemento <add> deve comparire su una sola riga.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>         <add type="Sample.YMLOperationBinding,Yml,              Version=1.0.0.0,Culture=neutral,              PublicKeyToken=6e55c64c6b897b30"/>      </serviceDescriptionFormatExtensionTypes>
       </webServices>
    </system.web>
    
  3. Aggiungere un elemento <soapExtensionReflectorTypes> con un elemento <add> che specifichi il nome e l'assembly della classe che estende il processo di generazione della descrizione del servizio nel file Web.config dell'applicazione Web.

    Nell'esempio di codice che segue viene configurata la Sample.YMLReflector in modo che venga eseguita con tutti i servizi Web XML che fanno capo al file Web.config. L'intero elemento <add> deve comparire su una sola riga.

    <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>
    

Per configurare la SDFE in modo che venga eseguita con un client di un servizio Web XML

  1. Installare l'assembly contenente la SDFE nella cache di assembly globale.

    A tal fine, l'assembly deve avere un nome sicuro. Per informazioni dettagliate sulla creazione di un assembly con nome sicuro, vedere Creazione e utilizzo degli assembly con nome sicuro. Per informazioni dettagliate sull'installazione di un assembly, vedere Installazione di un assembly nella cache di assembly globale.

  2. Aggiungere un elemento <serviceDescriptionFormatExtensionTypes> con un elemento <add> che specifichi il nome e l'assembly contenente la SDFE nel file Machine.config.

    Nell'esempio di codice che segue viene configurata la SDFE Sample.YMLOperationBinding in modo che venga eseguita ogni volta che si genera una classe proxy per i servizi Web XML sul computer.

    <system.web>
       <webServices>
          <serviceDescriptionFormatExtensionTypes>         <add type="Sample.YMLOperationBinding,Yml,              Version=1.0.0.0,Culture=neutral,              PublicKeyToken=6e55c64c6b897b30"/>      </serviceDescriptionFormatExtensionTypes>
       </webServices>
    </system.web>
    
  3. Aggiungere un elemento <soapExtensionImporterTypes> con un elemento <add> che specifichi il nome e l'assembly della classe che estende il processo di generazione della classe proxy nel file Machine.config.

    Nell'esempio di codice che segue viene configurata la Sample.YMLImporter in modo che venga eseguita ogni volta che si genera una classe proxy per i servizi Web XML sul computer.

    <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>
    

    **Nota   **Il metodo generato nella classe proxy viene utilizzato da un'applicazione client che comunica con il servizio Web XML, pertanto, se un attributo aggiunto da una SDFE risiede in un assembly non noto all'applicazione, verrà generato un errore del compilatore. Per risolvere l'errore del compilatore, se si utilizza Visual Studio .NET, aggiungere un riferimento all'assembly che contiene l'attributo, se invece si utilizza un compilatore della riga di comando, specificare l'assembly nella riga di comando del compilatore.

Esempio di codice SDFE completo

Nell'esempio di codice che segue viene riportata una SDFE che estende i processi di generazione della descrizione del servizio e del proxy includendovi dettagli riguardo una estensione SOAP YML.

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; }
   }
}

Vedere anche

Modifica del messaggio SOAP tramite estensioni SOAP | XmlFormatExtensionAttribute | XmlFormatExtensionPrefixAttribute | XmlFormatExtensionPointAttribute