Compartir a través de


Providing custom XML Schema files for resolving operation and type metadata

One of the frequently asked questions that I get from our TAP customers is about an ability to provide own schema files for resolving the metadata. In this post, I provide a simple example of accomplishing that. I will add more details in future posts as necessary.

At design-time, once the adapter consumer has selected one or more operations, the Contract Generation utility (i.e. Add Adapter Service Reference tool) makes a call into the adapter to resolve the metadata to return appropriate WSDL and XML Schema. This WSDL and XML Schema are used by the tool to then generate appropriate metadata (.NET code for .net project and Schema files for BizTalk projects) files. The key method at work is adapter's implementation class for IMetadataResolver with methods ResolveOperationMetadata and ResolveTypeMetadata. See HelloWorldAdapterMetadataResolverHandler sample class at the end of this post.

OperationMetadata and TypeMetadata classes were re-factored in the February CTP refresh of the WCF LOB Adapter SDK. OperationMetadata and TypeMetadata are abstract classes now. Use ParameterizedOperationMetadata and StructuredTypeMetadata, where the original metadata classes were being used. In order to provide custom Xml Schemas, Adapter Writer can provide custom implementation by sub-classing OperationMetadata and TypeMetadata classes.

In this sample, instead of defining a custom type called Greeting using StructuredTypeMetadata, I would like to use an existing schema representation. In that case, I define my own subclass from TypeMetadata and override the ExportXmlSchema method to provide the XML Schema for the Greeting type.

class MyCustomTypeMetadata : TypeMetadata

{

public MyCustomTypeMetadata(string typeId, string typeName) : base(typeId, typeName)

{

}

/// <summary>

/// Override the base ExportXmlSchema and provide own custom

/// XML Schema.

/// </summary>

/// <param name="schemaExportContext"></param>

/// <param name="metadataLookup"></param>

/// <param name="timeout"></param>

public override void ExportXmlSchema(XmlSchemaExportContext schemaExportContext, MetadataLookup metadataLookup, TimeSpan timeout)

{

if (schemaExportContext == null)

{

throw new AdapterException("schemaExportContext is null");

}

// Find a way to either read in a schema file or create XmlSchema

// object yourself

// for this example, use the type name to load a schema

XmlReader reader = XmlReader.Create(@"c:\temp\greeting.xsd");

XmlSchema schema = XmlSchema.Read(reader, null);

if (!IsComplexTypeAlreadyDefined(schemaExportContext.SchemaSet, schema))

{

schemaExportContext.SchemaSet.Add(schema);

schemaExportContext.NamespacePrefixSet.Add("mytypes", "sample://My.Samples.CodeSnippets/PreDefinedTypes");

}

reader.Close();

}

/// <summary>

/// Helper function to see if the schema is already defined in the

/// XmlSchemaSet.

/// </summary>

/// <param name="oldschemaset"></param>

/// <param name="newschema"></param>

/// <returns></returns>

public static bool IsComplexTypeAlreadyDefined(XmlSchemaSet oldschemaset, XmlSchema newschema)

{

// ensure correct namespace was defined in the passed-in schema

foreach (XmlSchema schema in oldschemaset.Schemas(newschema.TargetNamespace))

{

foreach (XmlSchemaObject newschemaObject in newschema.Items)

{

if (newschemaObject is XmlSchemaComplexType)

{

//check for the definition of complex type in the schemaset

foreach (XmlSchemaObject schemaObject in schema.Items)

{

XmlSchemaComplexType complexType = schemaObject as XmlSchemaComplexType;

// Definition of this Complex Type already exists

if (complexType != null && String.Compare(complexType.Name, ((XmlSchemaComplexType)newschemaObject).Name, false, System.Globalization.CultureInfo.InvariantCulture) == 0)

return true;

}

}

}

}

return false;

}

public override bool CanSetDefaultValue

{

get { return false; }

}

public override System.Xml.XmlReader CreateXmlReader(AdapterDataReader dataReader)

{

return null;

}

public override System.Xml.XmlDictionaryWriter CreateXmlWriter(AdapterDataWriter dataWriter)

{

return null;

}

}

Greeting.xsd

<xsd:schema xmlns:b="https://schemas.microsoft.com/BizTalk/2003" xmlns="sample://My.Samples.CodeSnippets/PreDefinedTypes" elementFormDefault="qualified" targetNamespace="sample://My.Samples.CodeSnippets/PreDefinedTypes" xmlns:xsd="https://www.w3.org/2001/XMLSchema">

<xsd:element name="greeting" type="Greeting" />

<xsd:complexType name="Greeting">

<xsd:sequence>

<xsd:element name="address" type="UsAddress" />

<xsd:element name="text" type="xsd:string" />

<xsd:element minOccurs="0" maxOccurs="1" name="gender" type="xsd:string" />

</xsd:sequence>

</xsd:complexType>

<xsd:simpleType name="PostalCode">

<xsd:restriction base="xsd:positiveInteger">

<xsd:pattern value="\d{5}" />

</xsd:restriction>

</xsd:simpleType>

<xsd:complexType name="UsAddress">

<xsd:sequence>

<xsd:element minOccurs="1" maxOccurs="1" name="street1" nillable="true" type="xsd:string" />

<xsd:element minOccurs="0" maxOccurs="1" name="street2" type="xsd:string" />

<xsd:element minOccurs="1" maxOccurs="1" name="city" type="xsd:string" />

<xsd:element name="zip" type="PostalCode" />

</xsd:sequence>

</xsd:complexType>

</xsd:schema>

      

Similarly, the ExportInputXmlSchema and ExportOutputXmlSchema can be overriden in the Adapter’s Operation Metadata to provide custom schema files. The Adapter Writer can use the metadataLookup.GetOperationDefinitionFromInputMessageAction to get data for looking up and using the appropriate schema files.

 

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.ServiceModel.Adapters.Common;

namespace Microsoft.WCF.Samples.Adapters

{

    public class SayHelloWorldOperationMetadata : OperationMetadata

    {

        // Constructor

        public SayHelloWorldOperationMetadata(string operationId, string displayName) : base(operationId, displayName);

        {

        }

        /// <summary>

        /// Use the request message schema for resolving request message for an operation.

        /// </summary>

        /// <param name="exportContext"></param>

        /// <param name="metadataLookup"></param>

        /// <param name="timespan"></param>

        public override void ExportInputXmlSchema(XmlSchemaExportContext exportContext, MetadataLookup metadataLookup, TimeSpan timespan)

      {

            base.ExportXmlSchema(exportContext, metadataLookup, timespan);

            XmlReader reader = XmlReader.Create("SayHelloWorldRequestMessage.xsd");

            XmlSchema schema = XmlSchema.Read(reader, null);

            if (!MetadataOperation.IsComplexTypeAlreadyDefined(exportContext.SchemaSet, schema))

                schemaExportContext.SchemaSet.Add(schema);

            reader.Close();

        }

        /// <summary>

        /// Use the response message schema for resolving response message for an operation.

        /// </summary>

        /// <param name="exportContext"></param>

        /// <param name="metadataLookup"></param>

        /// <param name="timespan"></param>

        public override void ExportOutputXmlSchema(XmlSchemaExportContext exportContext, MetadataLookup metadataLookup, TimeSpan timespan)

        {

            base.ExportXmlSchema(exportContext, metadataLookup, timespan);

            XmlReader reader = XmlReader.Create("SayHelloWorldResponseMessage.xsd");

            XmlSchema schema = XmlSchema.Read(reader, null);

            if (!MetadataOperation.IsComplexTypeAlreadyDefined(exportContext.SchemaSet, schema))

                schemaExportContext.SchemaSet.Add(schema);

            reader.Close();

        }

    }

}

 

The custom operation metadata and type metadata classes are used in the Adapter’s metadata resolver handler class.

 

/// -----------------------------------------------------------------------------------------------------------

/// Module : HelloWorldAdapterMetadataResolverHandler.cs

/// Description : Metadata Resolver Handler class which implements the HandlerBase and

///                IMetadataResolverHandler interface

/// -----------------------------------------------------------------------------------------------------------

#region Using Directives

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.ServiceModel.Adapters.Common;

using System.Xml.Schema;

#endregion

namespace Microsoft.WCF.Samples.Adapters

{

    public class HelloWorldAdapterMetadataResolverHandler : HelloWorldAdapterHandlerBase, IMetadataResolverHandler

    {

        public HelloWorldAdapterMetadataResolverHandler(HelloWorldAdapterConnection connection

            , MetadataLookup metadataLookup)

            : base(connection, metadataLookup)

        {

        }

        #region IMetadataResolver Members

        public bool IsOperationMetadataValid(string operationId, DateTime lastUpdatedTimestamp, TimeSpan timeout)

        {

            if ("SayHelloWorld".Equals(operationId))

            {

                return true;

            }

            return false;

        }

        public bool IsTypeMetadataValid(string typeId, DateTime lastUpdatedTimestamp, TimeSpan timeout)

        {

            return true;

        }

        public OperationMetadata ResolveOperationMetadata(string operationId, TimeSpan timeout, out TypeMetadataCollection extraTypeMetadataResolved)

        {

            extraTypeMetadataResolved = null;

            if ("Hello/SayHelloWorld".Equals(operationId))

   {

                // USE CUSTOM SCHEMA

                if (useCustomSchema)

                {

                    SayHelloWorldOperationMetadata om = new SayHelloWorldOperationMetadata("Hello/SayHelloWorld", "SayHelloWorld");

                    return om;

                }

                // USE LOB ADAPTER SDK METADATA OBJECT MODEL

                else

                {

                    ParameterizedOperationMetadata om = new ParameterizedOperationMetadata("Hello/SayHelloWorld", "SayHelloWorld");

                    om.OperationNamespace = HelloWorldAdapter.SERVICENAMESPACE;

                    // TODO - the node ID is not set

                    // syntax: String SayHelloWorld( String aName );

                    OperationParameter parm1 = new OperationParameter("aName", OperationParameterDirection.In, new SimpleQualifiedType(XmlTypeCode.String), false);

                    parm1.Description = "Hello World is said by this name.";

                    // Expected result: aName says Hello World

                    OperationResult result = new OperationResult(new SimpleQualifiedType(XmlTypeCode.String), false);

                    om.Parameters = new List<OperationParameter>();

                    om.Parameters.Add(parm1);

                    om.OperationResult = result;

                    return om;

                }

            }

            return null;

        }

        public TypeMetadata ResolveTypeMetadata(string typeId, TimeSpan timeout, out TypeMetadataCollection extraTypeMetadataResolved)

        {

            extraTypeMetadataResolved = null;

            return null;

        }

        #endregion IMetadataResolver Members

    }

}

Comments

  • Anonymous
    March 28, 2007
    The comment has been removed

  • Anonymous
    March 30, 2007
    Hi Aman, Thanks for your comment.  I will post a sample on this blog shortly.  We have added some documentation related to Metadata Interfaces and Metadata Object Model in the WCF LOB Adapter SDK Beta 2 Release’s help file ({Install Folder}DocumentsWCFLOBAdapterSDK.chm). If you have any urgent queries or are blocked at any time, please send your TAP Program Manager an email and he can get us in touch as well.   Time permitting, I will also keep blogging about new and most wanted topics. :-) I would also like to hear your feedback on the product, so we can keep improving. Regards, Sonu

  • Anonymous
    June 20, 2007
    WCF LOB Adapter SDK provides Metadata Object Model to define a contract for an operation. This object

  • Anonymous
    June 20, 2007
    Scenario: Adapter Developer needs to define an operation signature using XML Schema types not directly

  • Anonymous
    July 09, 2007
    The comment has been removed

  • Anonymous
    July 10, 2007
    Mike, Can you provide more detail about the error? It will help me understand the problem better.