Welcome to MSDN Blogs Sign in | Join | Help

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="http://schemas.microsoft.com/BizTalk/2003" xmlns="sample://My.Samples.CodeSnippets/PreDefinedTypes" elementFormDefault="qualified" targetNamespace="sample://My.Samples.CodeSnippets/PreDefinedTypes" xmlns:xsd="http://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

    }

}

 

Published Monday, March 26, 2007 7:48 PM by sonua

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Providing custom XML Schema files for resolving operation and type metadata

Hi Sonu,

Can you please point me to a resource which can help me in understanding the use of Metadata interfaces in this SDK. I need to start from very basic on how I can create an adapter for my LOB application which works on line of SQL,SAP etc..

I am a TAP customer and work for Major bank in New Zealand. Your help will be much appericiated.

Keep blogging!!!

-Aman

Wednesday, March 28, 2007 9:24 PM by Aman Gupta

# re: Providing custom XML Schema files for resolving operation and type metadata

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}\Documents\WCFLOBAdapterSDK.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

Saturday, March 31, 2007 2:40 AM by sonua

# 'WCF LOB Adapter SDK' metadata object model

WCF LOB Adapter SDK provides Metadata Object Model to define a contract for an operation. This object

Wednesday, June 20, 2007 12:48 PM by Developing adapters using WCF

# Metadata object model extensibility using ExportXmlSchema

Scenario: Adapter Developer needs to define an operation signature using XML Schema types not directly

Wednesday, June 20, 2007 1:59 PM by Developing adapters using WCF

# re: Providing custom XML Schema files for resolving operation and type metadata

Hi Sonu,

Thanks for the great blog,

getting to the point, I'm trying to use Complex types as output from an adapter operation, I use reflection to refelct a class library that contains some types.

the resolver works just fine, but I'm having a problem serializing the message back to the caller, i.e.: inside OutBoundHandler.Execute function.

is there anyway to instantiate this complex type and serialize inside the message?

Thanks

Monday, July 09, 2007 9:29 AM by Mike

# re: Providing custom XML Schema files for resolving operation and type metadata

Mike, Can you provide more detail about the error? It will help me understand the problem better.

Wednesday, July 11, 2007 2:59 AM by sonua

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker