Flatten your WSDL with this Custom ServiceHost for WCF

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