Christian Weyer: Smells like service spirit

What's first?

The Web services empire strikes back - Schema Importer Extensions

Part 1: The Web services empire strikes back - Introductory thoughts
Part 2: The Web services empire strikes back - Inner Workings
Part 3: The Web services empire strikes back - Web Services in Visual Studio 2005
Part 4: The Web services empire strikes back - WS-I BP Conformance
Part 5: The Web services empire strikes back - Custom XML Serialization
Part 6: The Web services empire strikes back - Proxy Type Sharing
Part 7: The Web services empire strikes back - Contract-first with .NET 'IDL'

Just imagine you have a new data class that provides a disconnected, in-memory cache of data from a relational data store and you are sure nobody else has ever implemented this. Now your instances of this class to be used to pass data between Web services. If you have read this small articles series carefully, you might want to say that you just have implement the IXmlSerializable interface for this type. This enables you to control the serialization and deserialization of instances of the class and provide whatever schema they want to expose for the type. Great. While this addresses serialization, deserialization and schema export, what about the actual programming experience on the client? The exact programming model that is available on the service side should also to be provided to consumers of Web services that utilize this new data type - at least on the .NET platform. There are cases where you just do not want to use simple data containers, but classes with a bit of logic in it. Please note, that I personally do not think this is a good design decision, but you cannot always have the best of breed design ideas implemented in your every day projects.

Essentially, the programming model should roundtrip the schema export on the service side to the schema import on the client process. Again, this is not about type fidelity like in .NET Remoting! It is just for enabling the Web services consumer developer with the same powerful programming model that may be exposed in the services internal implementation. The solution is to implement a SchemaImporterExtension-based extension that generates the same programming model on the client.

The so called Schema Import Extensions feature of ASMX v2 enables third-parties to plug into the schema import process. This process is invoked whenever a Web service proxy is produced either via the Add Web Reference .. dialog or wsdl.exe (or some other tool that uses the intrinsic classes). The schema import involves the creation of a source code representation of a graph of CLR types induced by the examination of the provided schema. These extensions are called on import to generate proxy code for schemas and WSDL. They are pluggable at the application and machine configuration level for dynamic deployment.

As you might imagine, this feature is the base for supporting new data types and controlling the schema generation for both the .NET Framework developers and you. For example there are already two pre-built implementations of the base class in the next version of the .NET Framework: System.Data.DataSetSchemaImporterExtension and System.Data.Design.TypedDataSetSchemaImporterExtension.

To make things a bit clearer lets take a look at an example.
When registered appropriately, this example SchemaImporterExtension takes care of providing the actual implementation for an Order type. There are two overloaded versions of the ImportSchemaType method. Here we use the version that explicitly expects the name and the namespace of a type as a string. When we encounter a certain type we just use CodeDoms functionality to import our .NET namespace for the Order type into the CodeDom code namespace. For ease of deployment we do not inject any source code into the generated proxy files but rather rely on referencing an external assembly. This assembly has to be deployed hand in hand with the schema importing assembly.

public class OrderSchemaImporterExtension : SchemaImporterExtension
{
  public override string ImportSchemaType(string name, string ns,
    XmlSchemaObject context, XmlSchemas schemas,
    XmlSchemaImporter importer, CodeNamespace codeNamespace,
    StringCollection references, CodeGenerationOptions options,
    ICodeGenerator codeGenerator)
  {
    if(name.Equals("Order") && ns.Equals("
http://orders/"))
    {
      references.Add("Order.dll");
      codeNamespace.Imports.Add(new
        CodeNamespaceImport("MSDNOnline.Samples"));
      return "MSDNOnline.Samples.Order";
    }
    return null;
  }

  public override CodeExpression ImportDefaultValue(
    string value, string type)
  {
    return new CodePrimitiveExpression(new Order());
  }
}


Comments

No Comments