Configuration and Metadata Support

This topic describes how to enable configuration and metadata support for bindings and binding elements.

Overview of Configuration and Metadata

This topic discusses the following tasks, which are optional items 1, 2, and 4 in the Developing Channels task list.

  • Enabling configuration file support for a binding element.

  • Enabling configuration file support for a binding.

  • Exporting WSDL and policy assertions for a binding element.

  • Identifying WSDL and policy assertions to insert and configure your binding or binding element.

For information about creating user-defined bindings and binding elements, see Creating User-Defined Bindings and Creating a BindingElement, respectively.

Adding Configuration Support

To enable configuration file support for a channel, you must implement two configuration sections, System.ServiceModel.Configuration.BindingElementExtensionElement, which enables configuration support for binding elements, and the System.ServiceModel.Configuration.StandardBindingElement and System.ServiceModel.Configuration.StandardBindingCollectionElement<TStandardBinding,TBindingConfiguration>, which enable configuration support for bindings.

An easier way to do this is to use the ConfigurationCodeGenerator sample tool to generate configuration code for your bindings and binding elements.

Extending BindingElementExtensionElement

The following example code is taken from the Transport: UDP sample. The UdpTransportElement is a BindingElementExtensionElement that exposes UdpTransportBindingElement to the configuration system. With a few basic overrides, the sample defines the configuration section name, the type of the binding element and how to create the binding element. Users can then register the extension section in a configuration file as follows.

<configuration>  
  <system.serviceModel>  
    <extensions>  
      <bindingElementExtensions>  
      <add name="udpTransport" type="Microsoft.ServiceModel.Samples.UdpTransportElement, UdpTransport" />  
      </bindingElementExtensions>  
    </extensions>  
  </system.serviceModel>  
</configuration>  

The extension can be referenced from custom bindings to use UDP as the transport.

<configuration>  
  <system.serviceModel>  
    <bindings>  
      <customBinding>  
       <binding configurationName="UdpCustomBinding">  
         <udpTransport/>  
       </binding>  
      </customBinding>  
    </bindings>  
  </system.serviceModel>  
</configuration>  

Adding Configuration for a Binding

The section SampleProfileUdpBindingCollectionElement is a StandardBindingCollectionElement<TStandardBinding,TBindingConfiguration> that exposes SampleProfileUdpBinding to the configuration system. The bulk of the implementation is delegated to the SampleProfileUdpBindingConfigurationElement, which derives from StandardBindingElement. The SampleProfileUdpBindingConfigurationElement has properties that correspond to the properties on SampleProfileUdpBinding, and functions to map from the ConfigurationElement binding. Finally, the OnApplyConfiguration method is overridden in the SampleProfileUdpBinding, as shown in the following sample code.

protected override void OnApplyConfiguration(string configurationName)  
{  
            if (binding == null)  
                throw new ArgumentNullException("binding");  
  
            if (binding.GetType() != typeof(SampleProfileUdpBinding))  
            {  
                var expectedType = typeof(SampleProfileUdpBinding).AssemblyQualifiedName;
                var typePassedIn = binding.GetType().AssemblyQualifiedName;
                throw new ArgumentException($"Invalid type for binding. Expected type: {expectedType}. Type passed in: {typePassedIn}.");  
            }  
            SampleProfileUdpBinding udpBinding = (SampleProfileUdpBinding)binding;  
  
            udpBinding.OrderedSession = this.OrderedSession;  
            udpBinding.ReliableSessionEnabled = this.ReliableSessionEnabled;  
            udpBinding.SessionInactivityTimeout = this.SessionInactivityTimeout;  
            if (this.ClientBaseAddress != null)  
                   udpBinding.ClientBaseAddress = ClientBaseAddress;  
}  

To register this handler with the configuration system, add the following section to the relevant configuration file.

<configuration>  
  <configSections>  
     <sectionGroup name="system.serviceModel">  
         <sectionGroup name="bindings">  
                 <section name="sampleProfileUdpBinding" type="Microsoft.ServiceModel.Samples.SampleProfileUdpBindingCollectionElement, UdpTransport" />  
         </sectionGroup>  
     </sectionGroup>  
  </configSections>  
</configuration>  

It can then be referenced from the <system.serviceModel> configuration section.

<configuration>  
  <system.serviceModel>  
    <client>  
      <endpoint configurationName="calculator"  
                address="soap.udp://localhost:8001/"
                bindingConfiguration="CalculatorServer"  
                binding="sampleProfileUdpBinding"
                contract= "Microsoft.ServiceModel.Samples.ICalculatorContract">  
      </endpoint>  
    </client>  
  </system.serviceModel>  
</configuration>  

Adding Metadata Support for a Binding Element

To integrate a channel into the metadata system, it must support both the import and export of policy. This allows tools such as ServiceModel Metadata Utility Tool (Svcutil.exe) to generate clients of the binding element.

Adding WSDL Support

The transport binding element in a binding is responsible for exporting and importing addressing information in metadata. When using a SOAP binding, the transport binding element should also export a correct transport URI in metadata. The following example code is taken from the Transport: UDP sample.

WSDL Export

To export addressing information, the UdpTransportBindingElement implements the System.ServiceModel.Description.IWsdlExportExtension interface. The IWsdlExportExtension.ExportEndpoint method adds the correct addressing information to the WSDL port.

if (context.WsdlPort != null)  
{  
    AddAddressToWsdlPort(context.WsdlPort, context.Endpoint.Address, encodingBindingElement.MessageVersion.Addressing);  
}  

The UdpTransportBindingElement implementation of the ExportEndpoint method also exports a transport URI when the endpoint uses a SOAP binding:

WsdlNS.SoapBinding soapBinding = GetSoapBinding(context, exporter);  
if (soapBinding != null)  
{  
    soapBinding.Transport = UdpPolicyStrings.UdpNamespace;  
}  

WSDL Import

To extend the WSDL import system to handle importing the addresses, add the following configuration to the configuration file for Svcutil.exe as shown in the Svcutil.exe.config file:

<configuration>  
  <system.serviceModel>  
    <client>  
      <metadata>  
        <wsdlImporters>  
          <extension type=" Microsoft.ServiceModel.Samples.UdpBindingElementImporter, UdpTransport" />  
        </wsdlImporters>  
      </metadata>  
    </client>  
  </system.serviceModel>  
</configuration>  

When running Svcutil.exe, there are two options for getting Svcutil.exe to load the WSDL import extensions:

  1. Point Svcutil.exe to the configuration file using the /SvcutilConfig:<file>.

  2. Add the configuration section to Svcutil.exe.config in the same directory as Svcutil.exe.

The UdpBindingElementImporter type implements the System.ServiceModel.Description.IWsdlImportExtension interface. The ImportEndpoint method imports the address from the WSDL port:

BindingElementCollection bindingElements = context.Endpoint.Binding.CreateBindingElements();  
TransportBindingElement transportBindingElement = bindingElements.Find<TransportBindingElement>();  
if (transportBindingElement is UdpTransportBindingElement)  
{  
    ImportAddress(context);  
}  

Adding Policy Support

The custom binding element can export policy assertions in the WSDL binding for a service endpoint to express the capabilities of that binding element. The following example code is taken from the Transport: UDP sample.

Policy Export

The UdpTransportBindingElement type implements System.ServiceModel.Description.IPolicyExportExtension to add support for exporting policy. As a result, System.ServiceModel.Description.MetadataExporter includes UdpTransportBindingElement in the generation of policy for any binding that includes it.

In IPolicyExportExtension.ExportPolicy, add an assertion for UDP and another assertion if the channel is in multicast mode. This is because multicast mode affects how the communication stack is constructed, and thus must be coordinated between both sides.

ICollection<XmlElement> bindingAssertions = context.GetBindingAssertions();  
XmlDocument xmlDocument = new XmlDocument();  
bindingAssertions.Add(xmlDocument.CreateElement(  
UdpPolicyStrings.Prefix, UdpPolicyStrings.TransportAssertion, UdpPolicyStrings.UdpNamespace));  
if (Multicast)  
{  
    bindingAssertions.Add(xmlDocument.CreateElement(  
UdpPolicyStrings.Prefix, UdpPolicyStrings.MulticastAssertion,     UdpPolicyStrings.UdpNamespace));  
}  

Because custom transport binding elements are responsible for handling addressing, the System.ServiceModel.Description.IPolicyExportExtension implementation on the UdpTransportBindingElement must also handle exporting the appropriate WS-Addressing policy assertions to indicate the version of WS-Addressing being used.

AddWSAddressingAssertion(context, encodingBindingElement.MessageVersion.Addressing);  

Policy Import

To extend the policy import system, add the following configuration to the configuration file for Svcutil.exe as shown in the Svcutil.exe.config file:

<configuration>  
  <system.serviceModel>  
    <client>  
      <metadata>  
        <policyImporters>  
          <extension type=" Microsoft.ServiceModel.Samples.UdpBindingElementImporter, UdpTransport" />  
        </policyImporters>  
      </metadata>  
    </client>  
  </system.serviceModel>  
</configuration>  

Then we implement System.ServiceModel.Description.IPolicyImportExtension from our registered class (UdpBindingElementImporter). In IPolicyImportExtension.ImportPolicy, examine the assertions in the appropriate namespace and process the ones for generating the transport and checking if it is multicast. In addition, remove the assertions that the importer handles from the list of binding assertions. Again, when running Svcutil.exe, there are two options for integration:

  1. Point Svcutil.exe to our configuration file using the /SvcutilConfig:<file>.

  2. Add the configuration section to Svcutil.exe.config in the same directory as Svcutil.exe.

Adding a Custom Standard Binding Importer

Svcutil.exe and the System.ServiceModel.Description.WsdlImporter type, by default, recognize and import system-provided bindings. Otherwise, the binding gets imported as a System.ServiceModel.Channels.CustomBinding instance. To enable Svcutil.exe and the WsdlImporter to import the SampleProfileUdpBinding the UdpBindingElementImporter also acts as a custom standard binding importer.

A custom standard binding importer implements the ImportEndpoint method on the System.ServiceModel.Description.IWsdlImportExtension interface to examine the System.ServiceModel.Channels.CustomBinding instance imported from metadata to see if it could have been generated by specific standard binding.

if (context.Endpoint.Binding is CustomBinding)  
{  
    Binding binding;  
    if (transportBindingElement is UdpTransportBindingElement)  
    {  
        //if TryCreate is true, the CustomBinding will be replace by a SampleProfileUdpBinding in the  
        //generated config file for better typed generation.  
        if (SampleProfileUdpBinding.TryCreate(bindingElements, out binding))  
        {  
            binding.Name = context.Endpoint.Binding.Name;  
            binding.Namespace = context.Endpoint.Binding.Namespace;  
            context.Endpoint.Binding = binding;  
        }  
    }  
}  

Generally, implementing a custom standard binding importer involves checking the properties of the imported binding elements to verify that only properties that could have been set by the standard binding have changed and all other properties are their defaults. A basic strategy for implementing a standard binding importer is to create an instance of the standard binding, propagate the properties from the binding elements to the standard binding instance that the standard binding supports, and the compare the binding elements from the standard binding with the imported binding elements.