Kirk Evans Blog

.NET From a Markup Perspective

Web Services Contract First: Drop Schema in app_code with a Custom Build Provider

I now have complete respect for Fritz’ reaction to custom build providers.

Web applications in Visual Studio 2005 allow you to drop items in the app_code directory in your project and classes are compiled in the background for you.  For instance, drop a WSDL file in there and you get a proxy for the web service, drop a schema and you get a DataSet.  I wanted to drop a schema in app_code and get an XML serializable class.

I cheated. 

I posted to an internal distribution list asking if anyone knew how to do this.  Rick Lievano pointed me to Fritz’ article.  I then pinged Elena Kharitidi, and she referenced creating a custom build provider as well.  Hmm… now, how do I get that done?  Then I remembered Daniel Cazzulino’s excellent article on XsdCodeGen.  A couple modifications between Fritz’ blog posting and Daniel’s article, and I had a jaw-dropping experience as well.

using System;
using System.Text;
using System.Web.Compilation;
using System.Web;
using System.Web.UI;
using System.CodeDom;
using System.Web.Hosting;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Msdn.Samples.Compilation
public class XsdClassBuildProvider : System.Web.Compilation.BuildProvider
public override void GenerateCode(AssemblyBuilder assemblyBuilder)
// HACK: Need to update this to get namespace from schema somehow. Good for blog code.
string ns = “Evans.Test”;
XmlSchema xsd = null;

using (Stream schemaStream = base.OpenStream(base.VirtualPath))
xsd = XmlSchema.Read(schemaStream, null);
XmlSchemaSet set = new XmlSchemaSet();

XmlSchemas schemas = new XmlSchemas();
XmlSchemaImporter importer = new XmlSchemaImporter(schemas);

CodeNamespace codeNamespace = new CodeNamespace(ns);

// Generate a CodeCompileUnit from the dataset
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();

XmlCodeExporter exporter = new XmlCodeExporter(codeNamespace);

foreach (XmlSchemaElement element in xsd.Elements.Values)
// Import the mapping first.
XmlTypeMapping mapping = importer.ImportTypeMapping(
// Export the code finally.

// Add the CodeCompileUnit to the compilation
assemblyBuilder.AddCodeCompileUnit(this, codeCompileUnit);

I built the class and added a reference from a test web service project.  Since there is already a BuildProvider registered for files with .XSD by default, you need to remove the existing mapping then add in your custom provider.  In the web service project, I added the following to configuration/system.web/compilation:

<compilation debug=”false”>
  <remove extension=”.xsd”/>
  <add appliesTo=”Code” extension=”.xsd” type=”Msdn.Samples.Compilation.XsdClassBuildProvider”/>

That was too easy.  I created a very simple schema and added it to the app_code directory in a web service:

<?xml version=”1.0″ encoding=”utf-8″?>
<xs:schema id=”test” targetNamespace=”” elementFormDefault=”qualified” xmlns=”” xmlns:mstns=”” xmlns:xs=”“>
    <xs:complexType name=”customerType”>
            <xs:element name=”customerID” type=”xs:string” />
            <xs:element name=”customerName” type=”xs:string” />
    <xs:element name=”customer” type=”customerType”>

Here is what the screen looked like when my jaw hit the floor as well.


Custom build provider in ASP.NET that provides strongly-typed access to XML serializable schemas

The reason this is so cool is because it removes the extra step of dropping back to command-line with XSD.EXE to use the /classes switch.  Now, you just drop the schema in there and you are good to go.