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:
Defining the XML to add to the service description.
Creating an SDFE class by deriving from the ServiceDescriptionFormatExtension class.
Writing code to extend the service description generation process.
Writing code to extend the proxy class generation process.
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
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 theyml: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>
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
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 namespacehttps://www.contoso.com/yml
is specified later when theYMLOperationBinding.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
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 thehttps://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
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
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
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()
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;
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);
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
Create a class that derives from SoapExtensionImporter.
Public Class YMLImporter Inherits SoapExtensionImporter
public class YMLImporter : SoapExtensionImporter
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)
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;
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 theYML
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
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.
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>
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
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.
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>
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