Inline XSD in WSDL with WCF
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 <wsdl:types/> 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 (<wsdl:import/>) 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
// <xsd:imports/>
//
List<XmlSchema> importsList = new List<XmlSchema>();
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 examine</param>
/// <param name="schemaSet">SchemaSet with all referenced schemas</param>
/// <param name="importsList">List to add imports to</param>
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);
}
}
}
}
/// <summary>
/// Remove any <xsd:imports/> in the schema
/// </summary>
/// <param name="schema">Schema to process</param>
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 <wsdl:imports/> that WCF might be generating, such as the case when the service implementation and the service contract interface are in different namespaces.
Technorati: WCF, Windows Communication Foundation, WCF Extensibility, Web Services, Interoperability, WSDL






I know this is an old article but the WSDL generated isn’t correct when you check the flat WSDL the types are not declared the same.
Here is the flat version
Here is the WCF normal version
I think this is what is causing problems in my client with guids been treated as strings but though I would post. Will give more feedback if I find out the reason behind this.
I don’t think XSD imports should be removed.
With XSD imports removed, if related data contracts(container DC and member DC) use different namespaces. WsdlImporter won’t be able to import data contracts back using DataContractSerializer.
[...] This popular blogpost provides a very simple endpoint behavior to flatten your WSDL and it works like a charm! Note that the WsdlExporter has a property GeneratedWsdlDocuments which returns a ServiceDescriptionCollection. The catch here is that this is a collection System.Web.Services.ServiceDescription instead of System.ServiceModel.Description.ServiceDescription. It took me a while to figure that out. [...]
how do you use this in ??
how do you use this in a web service project ??
I see all i need to do is somehow modify the web.config.
I tried using the WCF configuration editor however when selecting
- advanced/extensions/behaviour element extensions/new
It could not see the type in the assembly which I have placed the class InlineXsdInWsdlBehavior.cs
ok, i worked it out, i needed to implement the BehaviorExtensionElement abstract class thus;
public class InlineXsdInWsdlBehavior
: BehaviorExtensionElement, IWsdlExportExtension, IEndpointBehavior
then implementation is thus;
public override Type BehaviorType
{
get
{
return typeof(InlineXsdInWsdlBehavior);
}
}
protected override object CreateBehavior()
{
return new InlineXsdInWsdlBehavior();
}
Job done. works like a charm… other than now im just trying to work out how to get the WSDL to stop importing the xsd types…
Which still results in the following line in the WSDL
Gerrr!