I've said in the past that a large part of the apparent complexity of WSDL had nothing to do with WSDL at all; it was rather caused by the inline inclusion of XSDs in the section. I'm a big fan of creating your schemas separately and then importing them into your WSDL contracts to keep them clean and more maintainable. This is a technique I've used quite successfully in the past, by the way, particularly thanks to Thinktecture's excellent WSCF tool.

However, there's one problem with this, and it's interoperability. Even though it's been 3 years since I made that comment, a lot of tools still can't cope with imported XSDs in WSDL, or with WSDL imports () themselves. This is a real turn down and means that a lot of time you have to craft crufty (no pun intented!) alternative WSDLs for a client that include inline schemas to get around it.

The default WSDL generator in Windows Communication Foundation does what I think is the right thing by default, and that's generating individual XSD files for schemas (which you can get at using the ?xsd=xsdX syntax) and then importing those in the main WSDL generated. However, because of the interoperability issues, you might need sometimes that pesky WSDL with the inline schemas, and if you are into using the auto-generated WSDL (as most people do), then having to manually grab those WSDL and XSD and modify them by hand is a pain in the neck.

Fortunately, WCF gives us the wonderful IWsdlExportExtension extensiblity point, which I've discussed eariler on, and so makes it perfectly possible to tweak the generated WSDL so that schemas are embedded inline. Here's a sample extension to do just that:

//

// InlineXsdInWsdlBehavior.cs

//

// Author:

//    Tomas Restrepo (tomasr@mvps.org)

//

 

using System;

using System.Collections;

using System.Collections.Generic;

using System.Configuration;

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

{

   /// <summary>

   /// IEndpointBehavior implementation that will

   /// force WCF to generate all schemas inline into the

   /// generated WSDL files, instead of as individual files.

   /// summary>

   public class InlineXsdInWsdlBehavior

      : IWsdlExportExtension, IEndpointBehavior

   {

 

      #region IWsdlExportExtension Implementation

      //

      // IWsdlExportExtension Implementation

      //

      public void ExportContract(

            WsdlExporter exporter,

            WsdlContractConversionContext context

         )

      {

         // never called

      }

 

      public void ExportEndpoint(

            WsdlExporter exporter,

            WsdlEndpointConversionContext context

         )

      {

         XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas;

 

         foreach ( WsdlDescription wsdl in exporter.GeneratedWsdlDocuments )

         {

            //

            // Recursively find all schemas imported by this wsdl

            // and then add them. In the process, remove any

            //

            //

            List importsList = new List();

            foreach ( XmlSchema schema in wsdl.Types.Schemas )

            {

               AddImportedSchemas(schema, schemaSet, importsList);

            }

            wsdl.Types.Schemas.Clear();

            foreach ( XmlSchema schema in importsList )

            {

               RemoveXsdImports(schema);

               wsdl.Types.Schemas.Add(schema);

            }

         }

      }

 

      #endregion // IWsdlExportExtension Implementation

 

 

      #region Private Methods

      //

      // Private Methods

      //

 

      /// <summary>

      /// Recursively extract all the list of imported

      /// schemas

      /// summary>

      /// <param name="schema">Schema to examineparam>

      /// <param name="schemaSet">SchemaSet with all referenced schemasparam>

      /// <param name="importsList">List to add imports toparam>

      private void AddImportedSchemas(

         XmlSchema schema,

         XmlSchemaSet schemaSet,

         List 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);

               }

            }

         }

      }

 

      /// <summary>

      /// Remove any &lt;xsd:imports/&gt; in the schema

      /// summary>

      /// <param name="schema">Schema to processparam>

      private void RemoveXsdImports(XmlSchema schema)

      {

         for ( int i = 0; i < schema.Includes.Count; i++ )

         {

            if ( schema.Includes[i] is XmlSchemaImport )

               schema.Includes.RemoveAt(i--);

         }

      }

 

      #endregion // Private Methods

 

 

      #region IEndpointBehavior Implementation

      //

      // IContractBehavior Implementation

      //

      public void AddBindingParameters(

         ServiceEndpoint endpoint,

         BindingParameterCollection bindingParameters

         )

      {

         // not needed

      }

 

      public void ApplyClientBehavior(

         ServiceEndpoint endpoint,

         ClientRuntime clientRuntime

         )

      {

         // not needed

      }

 

      public void ApplyDispatchBehavior(

         ServiceEndpoint endpoint,

         EndpointDispatcher dispatcher

         )

      {

         // not needed

      }

 

      public void Validate(ServiceEndpoint endpoint)

      {

         // not needed

      }

 

      #endregion // IContractBehavior Implementation

 

   } // class InlineXsdInWsdlBehavior

 

} // namespace Winterdom.ServiceModel.Extensions

This will not get rid, however, of any that WCF might be generating, such as the case when the service implementation and the service contract interface are in different namespaces.


Tomas Restrepo

Software developer located in Colombia. Sr. PFE at Microsoft.