Yesterday I mentioned using a custom service host to flatten the WSDL that is generated by a WCF service. This is something Christian showed us all how to do a long while ago, to improve interoperability between WCF-implemented services and consumers written on other technology stacks. Flattening WSDL is important for Interop purposes becausse many tools don't digest modular WSDL very well. When I say modular WSDL, I mean WSDL that imports other WSDL's or XSDs.
I realized that I had never actually published the code for my custom WCF service host that flattens WSDL.
So here it is. [updated 146pm US/Pacific time based on Natasa's comment]
using System;
using System.Collections;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml.Schema;
using ServiceDescription = System.Web.Services.Description.ServiceDescription;
namespace Thinktecture.ServiceModel
{
public class FlatWsdl : IWsdlExportExtension, IEndpointBehavior
public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context) { }
public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas;
foreach (WsdlDescription wsdl in exporter.GeneratedWsdlDocuments)
List<XmlSchema> importsList = new List<XmlSchema>();
foreach (XmlSchema schema in wsdl.Types.Schemas)
AddImportedSchemas(schema, schemaSet, importsList);
if (importsList.Count == 0)
return;
wsdl.Types.Schemas.Clear();
foreach (XmlSchema schema in importsList)
RemoveXsdImports(schema);
wsdl.Types.Schemas.Add(schema);
}
private void AddImportedSchemas(XmlSchema schema, XmlSchemaSet schemaSet, List<XmlSchema> importsList)
foreach (XmlSchemaImport import in schema.Includes)
ICollection realSchemas =
schemaSet.Schemas(import.Namespace);
foreach (XmlSchema ixsd in realSchemas)
if (!importsList.Contains(ixsd))
importsList.Add(ixsd);
AddImportedSchemas(ixsd, schemaSet, importsList);
private void RemoveXsdImports(XmlSchema schema)
for (int i = 0; i < schema.Includes.Count; i++)
if (schema.Includes[i] is XmlSchemaImport)
schema.Includes.RemoveAt(i--);
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
public class FlatWsdlServiceHost : System.ServiceModel.ServiceHost
public FlatWsdlServiceHost() { }
public FlatWsdlServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
public FlatWsdlServiceHost(object singletonInstance, params Uri[] baseAddresses)
: base(singletonInstance, baseAddresses) { }
protected override void ApplyConfiguration()
Console.WriteLine("ApplyConfiguration (thread {0})",
System.Threading.Thread.CurrentThread.ManagedThreadId);
base.ApplyConfiguration();
InjectFlatWsdlExtension();
private void InjectFlatWsdlExtension()
foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
endpoint.Behaviors.Add(new FlatWsdl());
public sealed class FlatWsdlServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
return base.CreateServiceHost(constructorString, baseAddresses);
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
return new FlatWsdlServiceHost(serviceType, baseAddresses);
And to use this, you would specify something like this in your .svc file:
<%@ ServiceHost Language="C#" Factory="Thinktecture.ServiceModel.FlatWsdlServiceHostFactory" Service="Ionic.Samples.Webservices.WcfService1"%>