« Testing Code That Invokes WebServices | Main | Blog Content »
I've been playing for a few hours with implementing an IWsdlExportExtension for Windows Communication Foundation in RC1, and thought some of you might be interested in this little sample. Basically you implement IWsdlExportExtension as a way to customize the WSDL generation process for a given service contract or service endpoint. Here I'll show a simple extension that will add a default notice (say a copyright notice) to the WSDL and XSDs that WCF generates for a given service.
Where to implement it
IWsdlExportExtension needs to be implemented in either a Contract or an Endpoint Behavior itself. Either way is possible, but while trying this I found out some differences between both approaches that are not immediately apparent from the documentation.
The first is that the IWsdlExportExtension interface is composed of two different methods: ExportContract() and ExportEndpoint(). You need to be aware that in normal cases, the WCF runtime will call only one of this methods on your extension: If the extension is a contract behavior, then you'll want to implement ExportContract(); if it is an endpoint behavior you'll want to implement ExportEndpoint() instead.
My first try was to implement my custom extension as a contract behavior. That worked, but it only got me so far. What I discovered was that if the extension is a contract behavior [1], then when ExportContract() is called only the base WSDL description for the service contract has been created, but none of the supporting XML Schemas will be available yet. However, if you implement the extension as an endpoint behavior, then it seems this is done late enough in the game for you to be able to access (and modify) the generated schemas, which is a good thing.
For our sample extension we'll be implementing an endpoint behavior, which means our class will also need to implement IEndpointBehavior. The good news is that this implementation can be empty, since we don't actually need to implement any of the methods in the interface, and it's just a way to get WCF to use our IWsdlExportExtension.
Our Implementation
With that out of the way, implementing our extension is failry simple process. Here's what we want it to do:
So, here's our implementation with all the heavy lifting done inside the ExportEndpoint() method:
// // WsdlNoticeBehavior.cs //
// Author:
// Tomas Restrepo (tomasr@mvps.org)
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Web.Services;
using System.Web.Services.Description;
using WsdlDescription = System.Web.Services.Description.ServiceDescription;
namespace Winterdom.ServiceModel.Extensions
{
public class WsdlNoticeBehavior
: IWsdlExportExtension, IEndpointBehavior
#region IWsdlExportExtension Implementation
// IWsdlExportExtension Implementation
public void ExportContract(
WsdlExporter exporter,
WsdlContractConversionContext context
)
// never called
}
public void ExportEndpoint(
WsdlEndpointConversionContext context
foreach ( WsdlDescription wsdl in exporter.GeneratedWsdlDocuments )
// add the notice to the WSDL <documention/> element
wsdl.Documentation = Properties.Settings.Default.WsdlNotice;
// add a new XSD annotation with the documentation
// at the root of each generated schema
ICollection schemas = exporter.GeneratedXmlSchemas.Schemas();
foreach ( XmlSchema schema in schemas )
XmlDocument doc = new XmlDocument();
XmlNode node =
doc.CreateTextNode(Properties.Settings.Default.WsdlNotice);
XmlSchemaDocumentation comment = new XmlSchemaDocumentation();
comment.Markup = new XmlNode[] { node };
XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
annotation.Items.Add(comment);
schema.Items.Insert(0, annotation);
#endregion // IWsdlExportExtension Implementation
#region IEndpointBehavior Implementation
// IContractBehavior Implementation
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters
// not needed
public void ApplyClientBehavior(
ClientRuntime clientRuntime
public void ApplyDispatchBehavior(
EndpointDispatcher dispatcher
public void Validate(ServiceEndpoint endpoint)
#endregion // IContractBehavior Implementation
} // class WsdlNoticeBehavior
} // namespace Winterdom.ServiceModel.Extensions
Now we just need to add our behavior to the correct endpoint:
Uri uri = new Uri("http://localhost:8188/VoucherService");
ServiceHost host =
new ServiceHost(typeof(VoucherService), uri);
WSHttpBinding binding = new WSHttpBinding();
ServiceEndpoint endpoint =
host.AddServiceEndpoint(typeof(IVoucherService), binding, uri);
endpoint.Behaviors.Add(new WsdlNoticeBehavior());
host.Open();
Here's the configuration for our notice on our app.config file:
<userSettings>
<Winterdom.ServiceModel.Extensions.Properties.Settings>
<setting name="WsdlNotice" serializeAs="String">
<value>
This service description Copyright (C) 2006, Tomas Restrepo.
All rights reserved.
</value>
</setting>
</Winterdom.ServiceModel.Extensions.Properties.Settings>
</userSettings>
[1] A little off topic, but it might be worth mentioning that I'm not entirely comfortable with the way contract behaviors are implemented in WCF. Normally, one will want to define an attribute which implements IContractBehaviorAttribute to be able to attach your behavior to the specified contract in a nice way (from the API point of view). However, it feels totally unnatural for the attribute class to have to implement the actual behavior (including IWsdlExportExtension if that's your goal) in the same attribute class. I would've really expected to be able to define an attribute that instead determines the type that implemented the behavior instead, that being more in line with the pattern followed throughout the .NET Framework.
WCF, Windows Communication Foundation, WSDL
Tomas Restrepo is a software developer located in Colombia, South America. His interests include .NET, Connected Systems, PowerShell and lately dynamic programming languages. More...
email: tomas@winterdom.com msn: tomasr@passport.com
Copyright © 2002-2008, Tomas Restrepo.
Powered by: newtelligence dasBlog 2.2.8279.16125